aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-omap2/board-3430sdp.c9
-rw-r--r--arch/arm/mach-omap2/board-omap3beagle.c9
-rw-r--r--arch/arm/mach-omap2/board-omap3evm.c9
-rw-r--r--arch/arm/mach-omap2/board-omap3pandora.c9
-rw-r--r--arch/arm/mach-omap2/board-overo.c9
-rw-r--r--arch/arm/mach-omap2/board-zoom2.c9
-rw-r--r--arch/arm/plat-s3c/include/plat/audio.h45
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h2
-rw-r--r--drivers/mfd/Kconfig6
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/twl4030-codec.c241
-rw-r--r--drivers/mfd/twl4030-core.c14
-rw-r--r--include/linux/i2c/twl4030.h18
-rw-r--r--include/linux/mfd/twl4030-codec.h271
-rw-r--r--include/sound/soc-dai.h14
-rw-r--r--include/sound/soc-dapm.h17
-rw-r--r--include/sound/soc.h6
-rw-r--r--include/sound/tlv320dac33-plat.h20
-rw-r--r--include/sound/tpa6130a2-plat.h30
-rw-r--r--sound/soc/atmel/playpaq_wm8510.c2
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c2
-rw-r--r--sound/soc/au1x/psc-ac97.c49
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c7
-rw-r--r--sound/soc/blackfin/bf5xx-ad1938.c9
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c15
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.c9
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.c45
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.h11
-rw-r--r--sound/soc/codecs/Kconfig17
-rw-r--r--sound/soc/codecs/Makefile8
-rw-r--r--sound/soc/codecs/ak4671.c825
-rw-r--r--sound/soc/codecs/ak4671.h156
-rw-r--r--sound/soc/codecs/cs4270.c21
-rw-r--r--sound/soc/codecs/tlv320dac33.c1237
-rw-r--r--sound/soc/codecs/tlv320dac33.h267
-rw-r--r--sound/soc/codecs/tpa6130a2.c463
-rw-r--r--sound/soc/codecs/tpa6130a2.h61
-rw-r--r--sound/soc/codecs/twl4030.c404
-rw-r--r--sound/soc/codecs/twl4030.h242
-rw-r--r--sound/soc/codecs/wm8350.c19
-rw-r--r--sound/soc/codecs/wm8400.c20
-rw-r--r--sound/soc/codecs/wm8510.c4
-rw-r--r--sound/soc/codecs/wm8523.c17
-rw-r--r--sound/soc/codecs/wm8580.c21
-rw-r--r--sound/soc/codecs/wm8711.c642
-rw-r--r--sound/soc/codecs/wm8711.h42
-rw-r--r--sound/soc/codecs/wm8731.c85
-rw-r--r--sound/soc/codecs/wm8753.c39
-rw-r--r--sound/soc/codecs/wm8776.c34
-rw-r--r--sound/soc/codecs/wm8900.c21
-rw-r--r--sound/soc/codecs/wm8903.c17
-rw-r--r--sound/soc/codecs/wm8940.c21
-rw-r--r--sound/soc/codecs/wm8960.c21
-rw-r--r--sound/soc/codecs/wm8961.c17
-rw-r--r--sound/soc/codecs/wm8974.c27
-rw-r--r--sound/soc/codecs/wm8988.c34
-rw-r--r--sound/soc/codecs/wm8990.c4
-rw-r--r--sound/soc/codecs/wm8993.c38
-rw-r--r--sound/soc/codecs/wm9081.c17
-rw-r--r--sound/soc/codecs/wm9713.c4
-rw-r--r--sound/soc/codecs/wm_hubs.c35
-rw-r--r--sound/soc/codecs/wm_hubs.h5
-rw-r--r--sound/soc/davinci/Kconfig4
-rw-r--r--sound/soc/davinci/davinci-evm.c7
-rw-r--r--sound/soc/davinci/davinci-i2s.c2
-rw-r--r--sound/soc/davinci/davinci-mcasp.c17
-rw-r--r--sound/soc/davinci/davinci-pcm.c21
-rw-r--r--sound/soc/davinci/davinci-pcm.h1
-rw-r--r--sound/soc/imx/mx27vis_wm8974.c2
-rw-r--r--sound/soc/omap/Kconfig9
-rw-r--r--sound/soc/omap/Makefile2
-rw-r--r--sound/soc/omap/am3517evm.c202
-rw-r--r--sound/soc/omap/ams-delta.c4
-rw-r--r--sound/soc/omap/omap-pcm.c8
-rw-r--r--sound/soc/omap/omap3evm.c7
-rw-r--r--sound/soc/pxa/Kconfig3
-rw-r--r--sound/soc/pxa/magician.c2
-rw-r--r--sound/soc/pxa/pxa-ssp.c20
-rw-r--r--sound/soc/pxa/zylonite.c5
-rw-r--r--sound/soc/s3c24xx/Kconfig9
-rw-r--r--sound/soc/s3c24xx/Makefile2
-rw-r--r--sound/soc/s3c24xx/neo1973_gta02_wm8753.c8
-rw-r--r--sound/soc/s3c24xx/neo1973_wm8753.c9
-rw-r--r--sound/soc/s3c24xx/s3c-i2s-v2.c31
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.c1
-rw-r--r--sound/soc/s3c24xx/s3c2443-ac97.c1
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.c2
-rw-r--r--sound/soc/s3c24xx/s3c24xx-pcm.c1
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s.c20
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s.h1
-rw-r--r--sound/soc/s3c24xx/smdk64xx_wm8580.c268
-rw-r--r--sound/soc/sh/Kconfig1
-rw-r--r--sound/soc/sh/fsi.c234
-rw-r--r--sound/soc/soc-cache.c46
-rw-r--r--sound/soc/soc-core.c101
-rw-r--r--sound/soc/soc-dapm.c135
-rw-r--r--sound/soc/soc-jack.c3
97 files changed, 5775 insertions, 1187 deletions
diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c
index efaf053eba8..4f91f7a0896 100644
--- a/arch/arm/mach-omap2/board-3430sdp.c
+++ b/arch/arm/mach-omap2/board-3430sdp.c
@@ -410,6 +410,14 @@ static struct regulator_init_data sdp3430_vpll2 = {
.consumer_supplies = &sdp3430_vdvi_supply,
};
+static struct twl4030_codec_audio_data sdp3430_audio = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data sdp3430_codec = {
+ .audio = &sdp3430_audio,
+};
+
static struct twl4030_platform_data sdp3430_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@@ -420,6 +428,7 @@ static struct twl4030_platform_data sdp3430_twldata = {
.madc = &sdp3430_madc_data,
.keypad = &sdp3430_kp_data,
.usb = &sdp3430_usb_data,
+ .codec = &sdp3430_codec,
.vaux1 = &sdp3430_vaux1,
.vaux2 = &sdp3430_vaux2,
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index 70df6b4dbcd..2161d855fc9 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -254,6 +254,14 @@ static struct twl4030_usb_data beagle_usb_data = {
.usb_mode = T2_USB_MODE_ULPI,
};
+static struct twl4030_codec_audio_data beagle_audio_data = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data beagle_codec_data = {
+ .audio = &beagle_audio_data,
+};
+
static struct twl4030_platform_data beagle_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@@ -261,6 +269,7 @@ static struct twl4030_platform_data beagle_twldata = {
/* platform_data for children goes here */
.usb = &beagle_usb_data,
.gpio = &beagle_gpio_data,
+ .codec = &beagle_codec_data,
.vmmc1 = &beagle_vmmc1,
.vsim = &beagle_vsim,
.vdac = &beagle_vdac,
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
index e4ec0c59121..d9a61037ae3 100644
--- a/arch/arm/mach-omap2/board-omap3evm.c
+++ b/arch/arm/mach-omap2/board-omap3evm.c
@@ -194,6 +194,14 @@ static struct twl4030_madc_platform_data omap3evm_madc_data = {
.irq_line = 1,
};
+static struct twl4030_codec_audio_data omap3evm_audio_data = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data omap3evm_codec_data = {
+ .audio = &omap3evm_audio_data,
+};
+
static struct twl4030_platform_data omap3evm_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@@ -203,6 +211,7 @@ static struct twl4030_platform_data omap3evm_twldata = {
.madc = &omap3evm_madc_data,
.usb = &omap3evm_usb_data,
.gpio = &omap3evm_gpio_data,
+ .codec = &omap3evm_codec_data,
};
static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = {
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
index 7f6bf8772af..5036b56a21b 100644
--- a/arch/arm/mach-omap2/board-omap3pandora.c
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -281,11 +281,20 @@ static struct twl4030_usb_data omap3pandora_usb_data = {
.usb_mode = T2_USB_MODE_ULPI,
};
+static struct twl4030_codec_audio_data omap3pandora_audio_data = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data omap3pandora_codec_data = {
+ .audio = &omap3pandora_audio_data,
+};
+
static struct twl4030_platform_data omap3pandora_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
.gpio = &omap3pandora_gpio_data,
.usb = &omap3pandora_usb_data,
+ .codec = &omap3pandora_codec_data,
.vmmc1 = &pandora_vmmc1,
.vmmc2 = &pandora_vmmc2,
.keypad = &pandora_kp_data,
diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c
index 9917d2fddc2..dc550089755 100644
--- a/arch/arm/mach-omap2/board-overo.c
+++ b/arch/arm/mach-omap2/board-overo.c
@@ -329,6 +329,14 @@ static struct regulator_init_data overo_vmmc1 = {
.consumer_supplies = &overo_vmmc1_supply,
};
+static struct twl4030_codec_audio_data overo_audio_data = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data overo_codec_data = {
+ .audio = &overo_audio_data,
+};
+
/* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */
static struct twl4030_platform_data overo_twldata = {
@@ -336,6 +344,7 @@ static struct twl4030_platform_data overo_twldata = {
.irq_end = TWL4030_IRQ_END,
.gpio = &overo_gpio_data,
.usb = &overo_usb_data,
+ .codec = &overo_codec_data,
.vmmc1 = &overo_vmmc1,
};
diff --git a/arch/arm/mach-omap2/board-zoom2.c b/arch/arm/mach-omap2/board-zoom2.c
index b7b32208ced..f1b4e7cf550 100644
--- a/arch/arm/mach-omap2/board-zoom2.c
+++ b/arch/arm/mach-omap2/board-zoom2.c
@@ -229,6 +229,14 @@ static struct twl4030_madc_platform_data zoom2_madc_data = {
.irq_line = 1,
};
+static struct twl4030_codec_audio_data zoom2_audio_data = {
+ .audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data zoom2_codec_data = {
+ .audio = &zoom2_audio_data,
+};
+
static struct twl4030_platform_data zoom2_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@@ -239,6 +247,7 @@ static struct twl4030_platform_data zoom2_twldata = {
.usb = &zoom2_usb_data,
.gpio = &zoom2_gpio_data,
.keypad = &zoom2_kp_twl4030_data,
+ .codec = &zoom2_codec_data,
.vmmc1 = &zoom2_vmmc1,
.vmmc2 = &zoom2_vmmc2,
.vsim = &zoom2_vsim,
diff --git a/arch/arm/plat-s3c/include/plat/audio.h b/arch/arm/plat-s3c/include/plat/audio.h
deleted file mode 100644
index de0e8da48bc..00000000000
--- a/arch/arm/plat-s3c/include/plat/audio.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* arch/arm/mach-s3c2410/include/mach/audio.h
- *
- * Copyright (c) 2004-2005 Simtec Electronics
- * http://www.simtec.co.uk/products/SWLINUX/
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C24XX - Audio platfrom_device info
- *
- * 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.
-*/
-
-#ifndef __ASM_ARCH_AUDIO_H
-#define __ASM_ARCH_AUDIO_H __FILE__
-
-/* struct s3c24xx_iis_ops
- *
- * called from the s3c24xx audio core to deal with the architecture
- * or the codec's setup and control.
- *
- * the pointer to itself is passed through in case the caller wants to
- * embed this in an larger structure for easy reference to it's context.
-*/
-
-struct s3c24xx_iis_ops {
- struct module *owner;
-
- int (*startup)(struct s3c24xx_iis_ops *me);
- void (*shutdown)(struct s3c24xx_iis_ops *me);
- int (*suspend)(struct s3c24xx_iis_ops *me);
- int (*resume)(struct s3c24xx_iis_ops *me);
-
- int (*open)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
- int (*close)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
- int (*prepare)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm, struct snd_pcm_runtime *rt);
-};
-
-struct s3c24xx_platdata_iis {
- const char *codec_clk;
- struct s3c24xx_iis_ops *ops;
- int (*match_dev)(struct device *dev);
-};
-
-#endif /* __ASM_ARCH_AUDIO_H */
diff --git a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
index 07659dad174..abf2fbc2eb2 100644
--- a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
+++ b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
@@ -67,6 +67,8 @@
#define S3C2412_IISMOD_BCLK_MASK (3 << 1)
#define S3C2412_IISMOD_8BIT (1 << 0)
+#define S3C64XX_IISMOD_CDCLKCON (1 << 12)
+
#define S3C2412_IISPSR_PSREN (1 << 15)
#define S3C2412_IISFIC_TXFLUSH (1 << 15)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 570be139f9d..08f2d07bf56 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -121,6 +121,12 @@ config TWL4030_POWER
and load scripts controling which resources are switched off/on
or reset when a sleep, wakeup or warm reset event occurs.
+config TWL4030_CODEC
+ bool
+ depends on TWL4030_CORE
+ select MFD_CORE
+ default n
+
config MFD_TMIO
bool
default n
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f3b277b90d4..af0fc903cec 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
+obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
new file mode 100644
index 00000000000..97103078dc2
--- /dev/null
+++ b/drivers/mfd/twl4030-codec.c
@@ -0,0 +1,241 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * 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/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl4030-codec.h>
+
+#define TWL4030_CODEC_CELLS 2
+
+static struct platform_device *twl4030_codec_dev;
+
+struct twl4030_codec_resource {
+ int request_count;
+ u8 reg;
+ u8 mask;
+};
+
+struct twl4030_codec {
+ struct mutex mutex;
+ struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
+ struct mfd_cell cells[TWL4030_CODEC_CELLS];
+};
+
+/*
+ * Modify the resource, the function returns the content of the register
+ * after the modification.
+ */
+static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ u8 val;
+
+ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+ codec->resource[id].reg);
+
+ if (enable)
+ val |= codec->resource[id].mask;
+ else
+ val &= ~codec->resource[id].mask;
+
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ val, codec->resource[id].reg);
+
+ return val;
+}
+
+static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ u8 val;
+
+ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+ codec->resource[id].reg);
+
+ return val;
+}
+
+/*
+ * Enable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_enable_resource(enum twl4030_codec_res id)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ int val;
+
+ if (id >= TWL4030_CODEC_RES_MAX) {
+ dev_err(&twl4030_codec_dev->dev,
+ "Invalid resource ID (%u)\n", id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&codec->mutex);
+ if (!codec->resource[id].request_count)
+ /* Resource was disabled, enable it */
+ val = twl4030_codec_set_resource(id, 1);
+ else
+ val = twl4030_codec_get_resource(id);
+
+ codec->resource[id].request_count++;
+ mutex_unlock(&codec->mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
+
+/*
+ * Disable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_disable_resource(unsigned id)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ int val;
+
+ if (id >= TWL4030_CODEC_RES_MAX) {
+ dev_err(&twl4030_codec_dev->dev,
+ "Invalid resource ID (%u)\n", id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&codec->mutex);
+ if (!codec->resource[id].request_count) {
+ dev_err(&twl4030_codec_dev->dev,
+ "Resource has been disabled already (%u)\n", id);
+ mutex_unlock(&codec->mutex);
+ return -EPERM;
+ }
+ codec->resource[id].request_count--;
+
+ if (!codec->resource[id].request_count)
+ /* Resource can be disabled now */
+ val = twl4030_codec_set_resource(id, 0);
+ else
+ val = twl4030_codec_get_resource(id);
+
+ mutex_unlock(&codec->mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
+
+static int __devinit twl4030_codec_probe(struct platform_device *pdev)
+{
+ struct twl4030_codec *codec;
+ struct twl4030_codec_data *pdata = pdev->dev.platform_data;
+ struct mfd_cell *cell = NULL;
+ int ret, childs = 0;
+
+ codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, codec);
+
+ twl4030_codec_dev = pdev;
+ mutex_init(&codec->mutex);
+
+ /* Codec power */
+ codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
+ codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
+
+ /* PLL */
+ codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
+ codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
+
+ if (pdata->audio) {
+ cell = &codec->cells[childs];
+ cell->name = "twl4030_codec_audio";
+ cell->platform_data = pdata->audio;
+ cell->data_size = sizeof(*pdata->audio);
+ childs++;
+ }
+ if (pdata->vibra) {
+ cell = &codec->cells[childs];
+ cell->name = "twl4030_codec_vibra";
+ cell->platform_data = pdata->vibra;
+ cell->data_size = sizeof(*pdata->vibra);
+ childs++;
+ }
+
+ if (childs)
+ ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
+ childs, NULL, 0);
+ else {
+ dev_err(&pdev->dev, "No platform data found for childs\n");
+ ret = -ENODEV;
+ }
+
+ if (!ret)
+ return 0;
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(codec);
+ twl4030_codec_dev = NULL;
+ return ret;
+}
+
+static int __devexit twl4030_codec_remove(struct platform_device *pdev)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(pdev);
+
+ mfd_remove_devices(&pdev->dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(codec);
+ twl4030_codec_dev = NULL;
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:twl4030_codec");
+
+static struct platform_driver twl4030_codec_driver = {
+ .probe = twl4030_codec_probe,
+ .remove = __devexit_p(twl4030_codec_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "twl4030_codec",
+ },
+};
+
+static int __devinit twl4030_codec_init(void)
+{
+ return platform_driver_register(&twl4030_codec_driver);
+}
+module_init(twl4030_codec_init);
+
+static void __devexit twl4030_codec_exit(void)
+{
+ platform_driver_unregister(&twl4030_codec_driver);
+}
+module_exit(twl4030_codec_exit);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c
index e424cf6d8e9..0ee81e46bfb 100644
--- a/drivers/mfd/twl4030-core.c
+++ b/drivers/mfd/twl4030-core.c
@@ -114,6 +114,12 @@
#define twl_has_watchdog() false
#endif
+#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE)
+#define twl_has_codec() true
+#else
+#define twl_has_codec() false
+#endif
+
/* Triton Core internal information (BEGIN) */
/* Last - for index max*/
@@ -557,6 +563,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
+ if (twl_has_codec() && pdata->codec) {
+ child = add_child(1, "twl4030_codec",
+ pdata->codec, sizeof(*pdata->codec),
+ false, 0, 0);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
if (twl_has_regulator()) {
/*
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h
index 2d02dfd7076..42d6c722bd8 100644
--- a/include/linux/i2c/twl4030.h
+++ b/include/linux/i2c/twl4030.h
@@ -401,6 +401,23 @@ struct twl4030_power_data {
extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
+struct twl4030_codec_audio_data {
+ unsigned int audio_mclk;
+ unsigned int ramp_delay_value;
+ unsigned int hs_extmute:1;
+ void (*set_hs_extmute)(int mute);
+};
+
+struct twl4030_codec_vibra_data {
+ unsigned int audio_mclk;
+ unsigned int coexist;
+};
+
+struct twl4030_codec_data {
+ struct twl4030_codec_audio_data *audio;
+ struct twl4030_codec_vibra_data *vibra;
+};
+
struct twl4030_platform_data {
unsigned irq_base, irq_end;
struct twl4030_bci_platform_data *bci;
@@ -409,6 +426,7 @@ struct twl4030_platform_data {
struct twl4030_keypad_data *keypad;
struct twl4030_usb_data *usb;
struct twl4030_power_data *power;
+ struct twl4030_codec_data *codec;
/* LDO regulators */
struct regulator_init_data *vdac;
diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-codec.h
new file mode 100644
index 00000000000..ef0a3041d75
--- /dev/null
+++ b/include/linux/mfd/twl4030-codec.h
@@ -0,0 +1,271 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * 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
+ *
+ */
+
+#ifndef __TWL4030_CODEC_H__
+#define __TWL4030_CODEC_H__
+
+/* Codec registers */
+#define TWL4030_REG_CODEC_MODE 0x01
+#define TWL4030_REG_OPTION 0x02
+#define TWL4030_REG_UNKNOWN 0x03
+#define TWL4030_REG_MICBIAS_CTL 0x04
+#define TWL4030_REG_ANAMICL 0x05
+#define TWL4030_REG_ANAMICR 0x06
+#define TWL4030_REG_AVADC_CTL 0x07
+#define TWL4030_REG_ADCMICSEL 0x08
+#define TWL4030_REG_DIGMIXING 0x09
+#define TWL4030_REG_ATXL1PGA 0x0A
+#define TWL4030_REG_ATXR1PGA 0x0B
+#define TWL4030_REG_AVTXL2PGA 0x0C
+#define TWL4030_REG_AVTXR2PGA 0x0D
+#define TWL4030_REG_AUDIO_IF 0x0E
+#define TWL4030_REG_VOICE_IF 0x0F
+#define TWL4030_REG_ARXR1PGA 0x10
+#define TWL4030_REG_ARXL1PGA 0x11
+#define TWL4030_REG_ARXR2PGA 0x12
+#define TWL4030_REG_ARXL2PGA 0x13
+#define TWL4030_REG_VRXPGA 0x14
+#define TWL4030_REG_VSTPGA 0x15
+#define TWL4030_REG_VRX2ARXPGA 0x16
+#define TWL4030_REG_AVDAC_CTL 0x17
+#define TWL4030_REG_ARX2VTXPGA 0x18
+#define TWL4030_REG_ARXL1_APGA_CTL 0x19
+#define TWL4030_REG_ARXR1_APGA_CTL 0x1A
+#define TWL4030_REG_ARXL2_APGA_CTL 0x1B
+#define TWL4030_REG_ARXR2_APGA_CTL 0x1C
+#define TWL4030_REG_ATX2ARXPGA 0x1D
+#define TWL4030_REG_BT_IF 0x1E
+#define TWL4030_REG_BTPGA 0x1F
+#define TWL4030_REG_BTSTPGA 0x20
+#define TWL4030_REG_EAR_CTL 0x21
+#define TWL4030_REG_HS_SEL 0x22
+#define TWL4030_REG_HS_GAIN_SET 0x23
+#define TWL4030_REG_HS_POPN_SET 0x24
+#define TWL4030_REG_PREDL_CTL 0x25
+#define TWL4030_REG_PREDR_CTL 0x26
+#define TWL4030_REG_PRECKL_CTL 0x27
+#define TWL4030_REG_PRECKR_CTL 0x28
+#define TWL4030_REG_HFL_CTL 0x29
+#define TWL4030_REG_HFR_CTL 0x2A
+#define TWL4030_REG_ALC_CTL 0x2B
+#define TWL4030_REG_ALC_SET1 0x2C
+#define TWL4030_REG_ALC_SET2 0x2D
+#define TWL4030_REG_BOOST_CTL 0x2E
+#define TWL4030_REG_SOFTVOL_CTL 0x2F
+#define TWL4030_REG_DTMF_FREQSEL 0x30
+#define TWL4030_REG_DTMF_TONEXT1H 0x31
+#define TWL4030_REG_DTMF_TONEXT1L 0x32
+#define TWL4030_REG_DTMF_TONEXT2H 0x33
+#define TWL4030_REG_DTMF_TONEXT2L 0x34
+#define TWL4030_REG_DTMF_TONOFF 0x35
+#define TWL4030_REG_DTMF_WANONOFF 0x36
+#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37
+#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38
+#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39
+#define TWL4030_REG_APLL_CTL 0x3A
+#define TWL4030_REG_DTMF_CTL 0x3B
+#define TWL4030_REG_DTMF_PGA_CTL2 0x3C
+#define TWL4030_REG_DTMF_PGA_CTL1 0x3D
+#define TWL4030_REG_MISC_SET_1 0x3E
+#define TWL4030_REG_PCMBTMUX 0x3F
+#define TWL4030_REG_RX_PATH_SEL 0x43
+#define TWL4030_REG_VDL_APGA_CTL 0x44
+#define TWL4030_REG_VIBRA_CTL 0x45
+#define TWL4030_REG_VIBRA_SET 0x46
+#define TWL4030_REG_VIBRA_PWM_SET 0x47
+#define TWL4030_REG_ANAMIC_GAIN 0x48
+#define TWL4030_REG_MISC_SET_2 0x49
+
+/* Bitfield Definitions */
+
+/* TWL4030_CODEC_MODE (0x01) Fields */
+#define TWL4030_APLL_RATE 0xF0
+#define TWL4030_APLL_RATE_8000 0x00
+#define TWL4030_APLL_RATE_11025 0x10
+#define TWL4030_APLL_RATE_12000 0x20
+#define TWL4030_APLL_RATE_16000 0x40
+#define TWL4030_APLL_RATE_22050 0x50
+#define TWL4030_APLL_RATE_24000 0x60
+#define TWL4030_APLL_RATE_32000 0x80
+#define TWL4030_APLL_RATE_44100 0x90
+#define TWL4030_APLL_RATE_48000 0xA0
+#define TWL4030_APLL_RATE_96000 0xE0
+#define TWL4030_SEL_16K 0x08
+#define TWL4030_CODECPDZ 0x02
+#define TWL4030_OPT_MODE 0x01
+#define TWL4030_OPTION_1 (1 << 0)
+#define TWL4030_OPTION_2 (0 << 0)
+
+/* TWL4030_OPTION (0x02) Fields */
+#define TWL4030_ATXL1_EN (1 << 0)
+#define TWL4030_ATXR1_EN (1 << 1)
+#define TWL4030_ATXL2_VTXL_EN (1 << 2)
+#define TWL4030_ATXR2_VTXR_EN (1 << 3)
+#define TWL4030_ARXL1_VRX_EN (1 << 4)
+#define TWL4030_ARXR1_EN (1 << 5)
+#define TWL4030_ARXL2_EN (1 << 6)
+#define TWL4030_ARXR2_EN (1 << 7)
+
+/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
+#define TWL4030_MICBIAS2_CTL 0x40
+#define TWL4030_MICBIAS1_CTL 0x20
+#define TWL4030_HSMICBIAS_EN 0x04
+#define TWL4030_MICBIAS2_EN 0x02
+#define TWL4030_MICBIAS1_EN 0x01
+
+/* ANAMICL (0x05) Fields */
+#define TWL4030_CNCL_OFFSET_START 0x80
+#define TWL4030_OFFSET_CNCL_SEL 0x60
+#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00
+#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20
+#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40
+#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60
+#define TWL4030_MICAMPL_EN 0x10
+#define TWL4030_CKMIC_EN 0x08
+#define TWL4030_AUXL_EN 0x04
+#define TWL4030_HSMIC_EN 0x02
+#define TWL4030_MAINMIC_EN 0x01
+
+/* ANAMICR (0x06) Fields */
+#define TWL4030_MICAMPR_EN 0x10
+#define TWL4030_AUXR_EN 0x04
+#define TWL4030_SUBMIC_EN 0x01
+
+/* AVADC_CTL (0x07) Fields */
+#define TWL4030_ADCL_EN 0x08
+#define TWL4030_AVADC_CLK_PRIORITY 0x04
+#define TWL4030_ADCR_EN 0x02
+
+/* TWL4030_REG_ADCMICSEL (0x08) Fields */
+#define TWL4030_DIGMIC1_EN 0x08
+#define TWL4030_TX2IN_SEL 0x04
+#define TWL4030_DIGMIC0_EN 0x02
+#define TWL4030_TX1IN_SEL 0x01
+
+/* AUDIO_IF (0x0E) Fields */
+#define TWL4030_AIF_SLAVE_EN 0x80
+#define TWL4030_DATA_WIDTH 0x60
+#define TWL4030_DATA_WIDTH_16S_16W 0x00
+#define TWL4030_DATA_WIDTH_32S_16W 0x40
+#define TWL4030_DATA_WIDTH_32S_24W 0x60
+#define TWL4030_AIF_FORMAT 0x18
+#define TWL4030_AIF_FORMAT_CODEC 0x00
+#define TWL4030_AIF_FORMAT_LEFT 0x08
+#define TWL4030_AIF_FORMAT_RIGHT 0x10
+#define TWL4030_AIF_FORMAT_TDM 0x18
+#define TWL4030_AIF_TRI_EN 0x04
+#define TWL4030_CLK256FS_EN 0x02
+#define TWL4030_AIF_EN 0x01
+
+/* VOICE_IF (0x0F) Fields */
+#define TWL4030_VIF_SLAVE_EN 0x80
+#define TWL4030_VIF_DIN_EN 0x40
+#define TWL4030_VIF_DOUT_EN 0x20
+#define TWL4030_VIF_SWAP 0x10
+#define TWL4030_VIF_FORMAT 0x08
+#define TWL4030_VIF_TRI_EN 0x04
+#define TWL4030_VIF_SUB_EN 0x02
+#define TWL4030_VIF_EN 0x01
+
+/* EAR_CTL (0x21) */
+#define TWL4030_EAR_GAIN 0x30
+
+/* HS_GAIN_SET (0x23) Fields */
+#define TWL4030_HSR_GAIN 0x0C
+#define TWL4030_HSR_GAIN_PWR_DOWN 0x00
+#define TWL4030_HSR_GAIN_PLUS_6DB 0x04
+#define TWL4030_HSR_GAIN_0DB 0x08
+#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C
+#define TWL4030_HSL_GAIN 0x03
+#define TWL4030_HSL_GAIN_PWR_DOWN 0x00
+#define TWL4030_HSL_GAIN_PLUS_6DB 0x01
+#define TWL4030_HSL_GAIN_0DB 0x02
+#define TWL4030_HSL_GAIN_MINUS_6DB 0x03
+
+/* HS_POPN_SET (0x24) Fields */
+#define TWL4030_VMID_EN 0x40
+#define TWL4030_EXTMUTE 0x20
+#define TWL4030_RAMP_DELAY 0x1C
+#define TWL4030_RAMP_DELAY_20MS 0x00
+#define TWL4030_RAMP_DELAY_40MS 0x04
+#define TWL4030_RAMP_DELAY_81MS 0x08
+#define TWL4030_RAMP_DELAY_161MS 0x0C
+#define TWL4030_RAMP_DELAY_323MS 0x10
+#define TWL4030_RAMP_DELAY_645MS 0x14
+#define TWL4030_RAMP_DELAY_1291MS 0x18
+#define TWL4030_RAMP_DELAY_2581MS 0x1C
+#define TWL4030_RAMP_EN 0x02
+
+/* PREDL_CTL (0x25) */
+#define TWL4030_PREDL_GAIN 0x30
+
+/* PREDR_CTL (0x26) */
+#define TWL4030_PREDR_GAIN 0x30
+
+/* PRECKL_CTL (0x27) */
+#define TWL4030_PRECKL_GAIN 0x30
+
+/* PRECKR_CTL (0x28) */
+#define TWL4030_PRECKR_GAIN 0x30
+
+/* HFL_CTL (0x29, 0x2A) Fields */
+#define TWL4030_HF_CTL_HB_EN 0x04
+#define TWL4030_HF_CTL_LOOP_EN 0x08
+#define TWL4030_HF_CTL_RAMP_EN 0x10
+#define TWL4030_HF_CTL_REF_EN 0x20
+
+/* APLL_CTL (0x3A) Fields */
+#define TWL4030_APLL_EN 0x10
+#define TWL4030_APLL_INFREQ 0x0F
+#define TWL4030_APLL_INFREQ_19200KHZ 0x05
+#define TWL4030_APLL_INFREQ_26000KHZ 0x06
+#define TWL4030_APLL_INFREQ_38400KHZ 0x0F
+
+/* REG_MISC_SET_1 (0x3E) Fields */
+#define TWL4030_CLK64_EN 0x80
+#define TWL4030_SCRAMBLE_EN 0x40
+#define TWL4030_FMLOOP_EN 0x20
+#define TWL4030_SMOOTH_ANAVOL_EN 0x02
+#define TWL4030_DIGMIC_LR_SWAP_EN 0x01
+
+/* VIBRA_CTL (0x45) */
+#define TWL4030_VIBRA_EN 0x01
+#define TWL4030_VIBRA_DIR 0x02
+#define TWL4030_VIBRA_AUDIO_SEL_L1 (0x00 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_R1 (0x01 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_L2 (0x02 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_R2 (0x03 << 2)
+#define TWL4030_VIBRA_SEL 0x10
+#define TWL4030_VIBRA_DIR_SEL 0x20
+
+/* TWL4030 codec resource IDs */
+enum twl4030_codec_res {
+ TWL4030_CODEC_RES_POWER = 0,
+ TWL4030_CODEC_RES_APLL,
+ TWL4030_CODEC_RES_MAX,
+};
+
+int twl4030_codec_disable_resource(enum twl4030_codec_res id);
+int twl4030_codec_enable_resource(enum twl4030_codec_res id);
+
+#endif /* End of __TWL4030_CODEC_H__ */
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 97ca9af414d..ca24e7f7a3f 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -30,6 +30,7 @@ struct snd_pcm_substream;
#define SND_SOC_DAIFMT_DSP_A 3 /* L data MSB after FRM LRC */
#define SND_SOC_DAIFMT_DSP_B 4 /* L data MSB during FRM LRC */
#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
+#define SND_SOC_DAIFMT_PDM 6 /* Pulse density modulation */
/* left and right justified also known as MSB and LSB respectively */
#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J
@@ -106,7 +107,7 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
int div_id, int div);
int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out);
+ int pll_id, int source, unsigned int freq_in, unsigned int freq_out);
/* Digital Audio interface formatting */
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
@@ -114,6 +115,10 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot);
+
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
/* Digital Audio Interface mute */
@@ -136,8 +141,8 @@ struct snd_soc_dai_ops {
*/
int (*set_sysclk)(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir);
- int (*set_pll)(struct snd_soc_dai *dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out);
+ int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out);
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
/*
@@ -148,6 +153,9 @@ struct snd_soc_dai_ops {
int (*set_tdm_slot)(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width);
+ int (*set_channel_map)(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot);
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
/*
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index c1410e3191e..c5c95e1da65 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -206,6 +206,12 @@
.get = snd_soc_dapm_get_enum_double, \
.put = snd_soc_dapm_put_enum_double, \
.private_value = (unsigned long)&xenum }
+#define SOC_DAPM_ENUM_VIRT(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_dapm_get_enum_virt, \
+ .put = snd_soc_dapm_put_enum_virt, \
+ .private_value = (unsigned long)&xenum }
#define SOC_DAPM_VALUE_ENUM(xname, xenum) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_enum_double, \
@@ -260,6 +266,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
@@ -333,6 +343,10 @@ struct snd_soc_dapm_route {
const char *sink;
const char *control;
const char *source;
+
+ /* Note: currently only supported for links where source is a supply */
+ int (*connected)(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink);
};
/* dapm audio path between two widgets */
@@ -349,6 +363,9 @@ struct snd_soc_dapm_path {
u32 connect:1; /* source and sink widgets are connected */
u32 walked:1; /* path has been walked */
+ int (*connected)(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink);
+
struct list_head list_source;
struct list_head list_sink;
struct list_head list;
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 475cb7ed6be..b1245e3acdf 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -223,11 +223,6 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
int addr_bits, int data_bits,
enum snd_soc_control_type control);
-#ifdef CONFIG_PM
-int snd_soc_suspend_device(struct device *dev);
-int snd_soc_resume_device(struct device *dev);
-#endif
-
/* pcm <-> DAI connect */
void snd_soc_free_pcms(struct snd_soc_device *socdev);
int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
@@ -413,6 +408,7 @@ struct snd_soc_codec {
unsigned int num_dai;
#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_codec_root;
struct dentry *debugfs_reg;
struct dentry *debugfs_pop_time;
struct dentry *debugfs_dapm;
diff --git a/include/sound/tlv320dac33-plat.h b/include/sound/tlv320dac33-plat.h
new file mode 100644
index 00000000000..5858d06a7ff
--- /dev/null
+++ b/include/sound/tlv320dac33-plat.h
@@ -0,0 +1,20 @@
+/*
+ * Platform header for Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * 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.
+ */
+
+#ifndef __TLV320DAC33_PLAT_H
+#define __TLV320DAC33_PLAT_H
+
+struct tlv320dac33_platform_data {
+ int power_gpio;
+};
+
+#endif /* __TLV320DAC33_PLAT_H */
diff --git a/include/sound/tpa6130a2-plat.h b/include/sound/tpa6130a2-plat.h
new file mode 100644
index 00000000000..e8c901e749d
--- /dev/null
+++ b/include/sound/tpa6130a2-plat.h
@@ -0,0 +1,30 @@
+/*
+ * TPA6130A2 driver platform header
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Written by Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * 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
+ */
+
+#ifndef TPA6130A2_PLAT_H
+#define TPA6130A2_PLAT_H
+
+struct tpa6130a2_platform_data {
+ int power_gpio;
+};
+
+#endif
diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c
index 9eb610c2ba9..9df4c68ef00 100644
--- a/sound/soc/atmel/playpaq_wm8510.c
+++ b/sound/soc/atmel/playpaq_wm8510.c
@@ -268,7 +268,7 @@ static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
- ret = snd_soc_dai_set_pll(codec_dai, 0,
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0,
clk_get_rate(CODEC_CLK), pll_out);
if (ret < 0) {
pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 885ba012557..e028744c32c 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -207,7 +207,7 @@ static int __init at91sam9g20ek_init(void)
struct clk *pllb;
int ret;
- if (!machine_is_at91sam9g20ek())
+ if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))
return -ENODEV;
/*
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index a521aa90dde..2a06a9c548a 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -61,7 +61,8 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
- unsigned short data, retry, tmo;
+ unsigned short retry, tmo;
+ unsigned long data;
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
@@ -74,20 +75,26 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
AC97_CDC(pscdata));
au_sync();
- tmo = 2000;
- while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
- && --tmo)
- udelay(2);
+ tmo = 20;
+ do {
+ udelay(21);
+ if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
+ break;
+ } while (--tmo);
- data = au_readl(AC97_CDC(pscdata)) & 0xffff;
+ data = au_readl(AC97_CDC(pscdata));
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
mutex_unlock(&pscdata->lock);
+
+ if (reg != ((data >> 16) & 0x7f))
+ tmo = 1; /* wrong register, try again */
+
} while (--retry && !tmo);
- return retry ? data : 0xffff;
+ return retry ? data & 0xffff : 0xffff;
}
/* AC97 controller writes to codec register */
@@ -109,10 +116,12 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
AC97_CDC(pscdata));
au_sync();
- tmo = 2000;
- while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
- && --tmo)
- udelay(2);
+ tmo = 20;
+ do {
+ udelay(21);
+ if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
+ break;
+ } while (--tmo);
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
@@ -195,7 +204,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
unsigned long r, ro, stat;
- int chans, stype = SUBSTREAM_TYPE(substream);
+ int chans, t, stype = SUBSTREAM_TYPE(substream);
chans = params_channels(params);
@@ -237,8 +246,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
au_sync();
/* ...wait for it... */
- while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)
- asm volatile ("nop");
+ t = 100;
+ while ((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t)
+ msleep(1);
+
+ if (!t)
+ printk(KERN_ERR "PSC-AC97: can't disable!\n");
/* ...write config... */
au_writel(r, AC97_CFG(pscdata));
@@ -249,8 +262,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
au_sync();
/* ...and wait for ready bit */
- while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR))
- asm volatile ("nop");
+ t = 100;
+ while ((!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t)
+ msleep(1);
+
+ if (!t)
+ printk(KERN_ERR "PSC-AC97: can't enable!\n");
mutex_unlock(&pscdata->lock);
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
index cd361e304b0..0f45a3f56be 100644
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ b/sound/soc/blackfin/bf5xx-ad1836.c
@@ -52,6 +52,7 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
int ret = 0;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -65,6 +66,12 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
+ /* set cpu DAI channel mapping */
+ ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+ channel_map, ARRAY_SIZE(channel_map), channel_map);
+ if (ret < 0)
+ return ret;
+
return 0;
}
diff --git a/sound/soc/blackfin/bf5xx-ad1938.c b/sound/soc/blackfin/bf5xx-ad1938.c
index 08269e91810..2ef1e5013b8 100644
--- a/sound/soc/blackfin/bf5xx-ad1938.c
+++ b/sound/soc/blackfin/bf5xx-ad1938.c
@@ -61,6 +61,7 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
int ret = 0;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -75,7 +76,13 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
return ret;
/* set codec DAI slots, 8 channels, all channels are enabled */
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8);
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI channel mapping */
+ ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+ channel_map, ARRAY_SIZE(channel_map), channel_map);
if (ret < 0)
return ret;
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index 084b68884ad..3e6ada0dd1c 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -49,7 +49,6 @@ struct bf5xx_i2s_port {
u16 rcr1;
u16 tcr2;
u16 rcr2;
- int counter;
int configured;
};
@@ -133,16 +132,6 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return ret;
}
-static int bf5xx_i2s_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- pr_debug("%s enter\n", __func__);
-
- /*this counter is used for counting how many pcm streams are opened*/
- bf5xx_i2s.counter++;
- return 0;
-}
-
static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -201,9 +190,8 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
- bf5xx_i2s.counter--;
/* No active stream, SPORT is allowed to be configured again. */
- if (!bf5xx_i2s.counter)
+ if (!dai->active)
bf5xx_i2s.configured = 0;
}
@@ -284,7 +272,6 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
- .startup = bf5xx_i2s_startup,
.shutdown = bf5xx_i2s_shutdown,
.hw_params = bf5xx_i2s_hw_params,
.set_fmt = bf5xx_i2s_set_dai_fmt,
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
index ccb5e823bd1..a8c73cbbd68 100644
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -43,7 +43,7 @@
#include "bf5xx-tdm.h"
#include "bf5xx-sport.h"
-#define PCM_BUFFER_MAX 0x10000
+#define PCM_BUFFER_MAX 0x8000
#define FRAGMENT_SIZE_MIN (4*1024)
#define FRAGMENTS_MIN 2
#define FRAGMENTS_MAX 32
@@ -177,6 +177,9 @@ out:
static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ struct bf5xx_tdm_port *tdm_port = sport->private_data;
unsigned int *src;
unsigned int *dst;
int i;
@@ -188,7 +191,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
dst += pos * 8;
while (count--) {
for (i = 0; i < substream->runtime->channels; i++)
- *(dst + i) = *src++;
+ *(dst + tdm_port->tx_map[i]) = *src++;
dst += 8;
}
} else {
@@ -198,7 +201,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
src += pos * 8;
while (count--) {
for (i = 0; i < substream->runtime->channels; i++)
- *dst++ = *(src+i);
+ *dst++ = *(src + tdm_port->rx_map[i]);
src += 8;
}
}
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
index ff546e91a22..4b360124083 100644
--- a/sound/soc/blackfin/bf5xx-tdm.c
+++ b/sound/soc/blackfin/bf5xx-tdm.c
@@ -46,14 +46,6 @@
#include "bf5xx-sport.h"
#include "bf5xx-tdm.h"
-struct bf5xx_tdm_port {
- u16 tcr1;
- u16 rcr1;
- u16 tcr2;
- u16 rcr2;
- int configured;
-};
-
static struct bf5xx_tdm_port bf5xx_tdm;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
@@ -181,6 +173,40 @@ static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
bf5xx_tdm.configured = 0;
}
+static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ int i;
+ unsigned int slot;
+ unsigned int tx_mapped = 0, rx_mapped = 0;
+
+ if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
+ (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
+ return -EINVAL;
+
+ for (i = 0; i < tx_num; i++) {
+ slot = tx_slot[i];
+ if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+ (!(tx_mapped & (1 << slot)))) {
+ bf5xx_tdm.tx_map[i] = slot;
+ tx_mapped |= 1 << slot;
+ } else
+ return -EINVAL;
+ }
+ for (i = 0; i < rx_num; i++) {
+ slot = rx_slot[i];
+ if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+ (!(rx_mapped & (1 << slot)))) {
+ bf5xx_tdm.rx_map[i] = slot;
+ rx_mapped |= 1 << slot;
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_PM
static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
{
@@ -235,6 +261,7 @@ static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
.hw_params = bf5xx_tdm_hw_params,
.set_fmt = bf5xx_tdm_set_dai_fmt,
.shutdown = bf5xx_tdm_shutdown,
+ .set_channel_map = bf5xx_tdm_set_channel_map,
};
struct snd_soc_dai bf5xx_tdm_dai = {
@@ -300,6 +327,8 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
pr_err("Failed to register DAI: %d\n", ret);
goto sport_config_err;
}
+
+ sport_handle->private_data = &bf5xx_tdm;
return 0;
sport_config_err:
diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h
index 618ec3d90cd..04189a18c1b 100644
--- a/sound/soc/blackfin/bf5xx-tdm.h
+++ b/sound/soc/blackfin/bf5xx-tdm.h
@@ -9,6 +9,17 @@
#ifndef _BF5XX_TDM_H
#define _BF5XX_TDM_H
+#define BFIN_TDM_DAI_MAX_SLOTS 8
+struct bf5xx_tdm_port {
+ u16 tcr1;
+ u16 rcr1;
+ u16 tcr2;
+ u16 rcr2;
+ unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS];
+ unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS];
+ int configured;
+};
+
extern struct snd_soc_dai bf5xx_tdm_dai;
#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0edca93af3b..3df3497335b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -19,6 +19,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4642 if I2C
+ select SND_SOC_AK4671 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
@@ -28,6 +29,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC3X if I2C
+ select SND_SOC_TPA6130A2 if I2C
+ select SND_SOC_TLV320DAC33 if I2C
select SND_SOC_TWL4030 if TWL4030_CORE
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
@@ -36,6 +39,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8523 if I2C
select SND_SOC_WM8580 if I2C
+ select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
@@ -96,6 +100,9 @@ config SND_SOC_AK4535
config SND_SOC_AK4642
tristate
+config SND_SOC_AK4671
+ tristate
+
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
@@ -136,7 +143,11 @@ config SND_SOC_TLV320AIC26
config SND_SOC_TLV320AIC3X
tristate
+config SND_SOC_TLV320DAC33
+ tristate
+
config SND_SOC_TWL4030
+ select TWL4030_CODEC
tristate
config SND_SOC_UDA134X
@@ -160,6 +171,9 @@ config SND_SOC_WM8523
config SND_SOC_WM8580
tristate
+config SND_SOC_WM8711
+ tristate
+
config SND_SOC_WM8728
tristate
@@ -220,3 +234,6 @@ config SND_SOC_WM9713
# Amp
config SND_SOC_MAX9877
tristate
+
+config SND_SOC_TPA6130A2
+ tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index fb4af28486b..8f519ee9600 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -6,6 +6,7 @@ snd-soc-ad73311-objs := ad73311.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4642-objs := ak4642.o
+snd-soc-ak4671-objs := ak4671.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-l3-objs := l3.o
@@ -16,6 +17,7 @@ snd-soc-stac9766-objs := stac9766.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-tlv320dac33-objs := tlv320dac33.o
snd-soc-twl4030-objs := twl4030.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
@@ -24,6 +26,7 @@ snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8523-objs := wm8523.o
snd-soc-wm8580-objs := wm8580.o
+snd-soc-wm8711-objs := wm8711.o
snd-soc-wm8728-objs := wm8728.o
snd-soc-wm8731-objs := wm8731.o
snd-soc-wm8750-objs := wm8750.o
@@ -47,6 +50,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.o
+snd-soc-tpa6130a2-objs := tpa6130a2.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
@@ -56,6 +60,7 @@ obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
+obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
@@ -66,6 +71,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
@@ -74,6 +80,7 @@ obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
+obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
@@ -97,3 +104,4 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
new file mode 100644
index 00000000000..b61214d1c5d
--- /dev/null
+++ b/sound/soc/codecs/ak4671.c
@@ -0,0 +1,825 @@
+/*
+ * ak4671.c -- audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "ak4671.h"
+
+static struct snd_soc_codec *ak4671_codec;
+
+/* codec private data */
+struct ak4671_priv {
+ struct snd_soc_codec codec;
+ u8 reg_cache[AK4671_CACHEREGNUM];
+};
+
+/* ak4671 register cache & default register settings */
+static const u8 ak4671_reg[AK4671_CACHEREGNUM] = {
+ 0x00, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */
+ 0xf6, /* AK4671_PLL_MODE_SELECT0 (0x01) */
+ 0x00, /* AK4671_PLL_MODE_SELECT1 (0x02) */
+ 0x02, /* AK4671_FORMAT_SELECT (0x03) */
+ 0x00, /* AK4671_MIC_SIGNAL_SELECT (0x04) */
+ 0x55, /* AK4671_MIC_AMP_GAIN (0x05) */
+ 0x00, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */
+ 0x00, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */
+ 0xb5, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */
+ 0x00, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */
+ 0x00, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */
+ 0x00, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */
+ 0x00, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */
+ 0x00, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */
+ 0x00, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */
+ 0x00, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */
+ 0x00, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */
+ 0x80, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */
+ 0x91, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */
+ 0x91, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */
+ 0xe1, /* AK4671_ALC_REFERENCE_SELECT (0x14) */
+ 0x00, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */
+ 0x00, /* AK4671_ALC_TIMER_SELECT (0x16) */
+ 0x00, /* AK4671_ALC_MODE_CONTROL (0x17) */
+ 0x02, /* AK4671_MODE_CONTROL1 (0x18) */
+ 0x01, /* AK4671_MODE_CONTROL2 (0x19) */
+ 0x18, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */
+ 0x18, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */
+ 0x00, /* AK4671_SIDETONE_A_CONTROL (0x1c) */
+ 0x02, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */
+ 0x00, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */
+ 0x00, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */
+ 0x00, /* AK4671_FIL3_COEFFICIENT2 (0x20) */
+ 0x00, /* AK4671_FIL3_COEFFICIENT3 (0x21) */
+ 0x00, /* AK4671_EQ_COEFFICIENT0 (0x22) */
+ 0x00, /* AK4671_EQ_COEFFICIENT1 (0x23) */
+ 0x00, /* AK4671_EQ_COEFFICIENT2 (0x24) */
+ 0x00, /* AK4671_EQ_COEFFICIENT3 (0x25) */
+ 0x00, /* AK4671_EQ_COEFFICIENT4 (0x26) */
+ 0x00, /* AK4671_EQ_COEFFICIENT5 (0x27) */
+ 0xa9, /* AK4671_FIL1_COEFFICIENT0 (0x28) */
+ 0x1f, /* AK4671_FIL1_COEFFICIENT1 (0x29) */
+ 0xad, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */
+ 0x20, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */
+ 0x00, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */
+ 0x00, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */
+ 0x00, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */
+ 0x00, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */
+ 0x00, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */
+ 0x00, /* this register not used */
+ 0x00, /* AK4671_E1_COEFFICIENT0 (0x32) */
+ 0x00, /* AK4671_E1_COEFFICIENT1 (0x33) */
+ 0x00, /* AK4671_E1_COEFFICIENT2 (0x34) */
+ 0x00, /* AK4671_E1_COEFFICIENT3 (0x35) */
+ 0x00, /* AK4671_E1_COEFFICIENT4 (0x36) */
+ 0x00, /* AK4671_E1_COEFFICIENT5 (0x37) */
+ 0x00, /* AK4671_E2_COEFFICIENT0 (0x38) */
+ 0x00, /* AK4671_E2_COEFFICIENT1 (0x39) */
+ 0x00, /* AK4671_E2_COEFFICIENT2 (0x3a) */
+ 0x00, /* AK4671_E2_COEFFICIENT3 (0x3b) */
+ 0x00, /* AK4671_E2_COEFFICIENT4 (0x3c) */
+ 0x00, /* AK4671_E2_COEFFICIENT5 (0x3d) */
+ 0x00, /* AK4671_E3_COEFFICIENT0 (0x3e) */
+ 0x00, /* AK4671_E3_COEFFICIENT1 (0x3f) */
+ 0x00, /* AK4671_E3_COEFFICIENT2 (0x40) */
+ 0x00, /* AK4671_E3_COEFFICIENT3 (0x41) */
+ 0x00, /* AK4671_E3_COEFFICIENT4 (0x42) */
+ 0x00, /* AK4671_E3_COEFFICIENT5 (0x43) */
+ 0x00, /* AK4671_E4_COEFFICIENT0 (0x44) */
+ 0x00, /* AK4671_E4_COEFFICIENT1 (0x45) */
+ 0x00, /* AK4671_E4_COEFFICIENT2 (0x46) */
+ 0x00, /* AK4671_E4_COEFFICIENT3 (0x47) */
+ 0x00, /* AK4671_E4_COEFFICIENT4 (0x48) */
+ 0x00, /* AK4671_E4_COEFFICIENT5 (0x49) */
+ 0x00, /* AK4671_E5_COEFFICIENT0 (0x4a) */
+ 0x00, /* AK4671_E5_COEFFICIENT1 (0x4b) */
+ 0x00, /* AK4671_E5_COEFFICIENT2 (0x4c) */
+ 0x00, /* AK4671_E5_COEFFICIENT3 (0x4d) */
+ 0x00, /* AK4671_E5_COEFFICIENT4 (0x4e) */
+ 0x00, /* AK4671_E5_COEFFICIENT5 (0x4f) */
+ 0x88, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */
+ 0x88, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */
+ 0x08, /* AK4671_EQ_CONTRO_10KHZ (0x52) */
+ 0x00, /* AK4671_PCM_IF_CONTROL0 (0x53) */
+ 0x00, /* AK4671_PCM_IF_CONTROL1 (0x54) */
+ 0x00, /* AK4671_PCM_IF_CONTROL2 (0x55) */
+ 0x18, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */
+ 0x18, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */
+ 0x00, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */
+ 0x00, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */
+ 0x00, /* AK4671_SAR_ADC_CONTROL (0x5a) */
+};
+
+/*
+ * LOUT1/ROUT1 output volume control:
+ * from -24 to 6 dB in 6 dB steps (mute instead of -30 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1);
+
+/*
+ * LOUT2/ROUT2 output volume control:
+ * from -33 to 6 dB in 3 dB steps (mute instead of -33 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1);
+
+/*
+ * LOUT3/ROUT3 output volume control:
+ * from -6 to 3 dB in 3 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0);
+
+/*
+ * Mic amp gain control:
+ * from -15 to 30 dB in 3 dB steps
+ * REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not
+ * available
+ */
+static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new ak4671_snd_controls[] = {
+ /* Common playback gain controls */
+ SOC_SINGLE_TLV("Line Output1 Playback Volume",
+ AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv),
+ SOC_SINGLE_TLV("Headphone Output2 Playback Volume",
+ AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv),
+ SOC_SINGLE_TLV("Line Output3 Playback Volume",
+ AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv),
+
+ /* Common capture gain controls */
+ SOC_DOUBLE_TLV("Mic Amp Capture Volume",
+ AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv),
+};
+
+/* event handlers */
+static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u8 reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+ reg |= AK4671_MUTEN;
+ snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+ reg &= ~AK4671_MUTEN;
+ snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+ break;
+ }
+
+ return 0;
+}
+
+/* Output Mixers */
+static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0),
+ SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0),
+ SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0),
+ SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0),
+ SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0),
+ SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+/* Input MUXs */
+static const char *ak4671_lin_mux_texts[] =
+ {"LIN1", "LIN2", "LIN3", "LIN4"};
+static const struct soc_enum ak4671_lin_mux_enum =
+ SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0,
+ ARRAY_SIZE(ak4671_lin_mux_texts),
+ ak4671_lin_mux_texts);
+static const struct snd_kcontrol_new ak4671_lin_mux_control =
+ SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
+
+static const char *ak4671_rin_mux_texts[] =
+ {"RIN1", "RIN2", "RIN3", "RIN4"};
+static const struct soc_enum ak4671_rin_mux_enum =
+ SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2,
+ ARRAY_SIZE(ak4671_rin_mux_texts),
+ ak4671_rin_mux_texts);
+static const struct snd_kcontrol_new ak4671_rin_mux_control =
+ SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
+
+static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("LIN1"),
+ SND_SOC_DAPM_INPUT("RIN1"),
+ SND_SOC_DAPM_INPUT("LIN2"),
+ SND_SOC_DAPM_INPUT("RIN2"),
+ SND_SOC_DAPM_INPUT("LIN3"),
+ SND_SOC_DAPM_INPUT("RIN3"),
+ SND_SOC_DAPM_INPUT("LIN4"),
+ SND_SOC_DAPM_INPUT("RIN4"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("LOUT1"),
+ SND_SOC_DAPM_OUTPUT("ROUT1"),
+ SND_SOC_DAPM_OUTPUT("LOUT2"),
+ SND_SOC_DAPM_OUTPUT("ROUT2"),
+ SND_SOC_DAPM_OUTPUT("LOUT3"),
+ SND_SOC_DAPM_OUTPUT("ROUT3"),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback",
+ AK4671_AD_DA_POWER_MANAGEMENT, 6, 0),
+ SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback",
+ AK4671_AD_DA_POWER_MANAGEMENT, 7, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture",
+ AK4671_AD_DA_POWER_MANAGEMENT, 4, 0),
+ SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture",
+ AK4671_AD_DA_POWER_MANAGEMENT, 5, 0),
+
+ /* PGA */
+ SND_SOC_DAPM_PGA("LOUT2 Mix Amp",
+ AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ROUT2 Mix Amp",
+ AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("LIN1 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN1 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LIN2 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN2 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LIN3 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN3 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LIN4 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RIN4 Mixing Circuit",
+ AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0),
+
+ /* Output Mixers */
+ SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0,
+ &ak4671_lout1_mixer_controls[0],
+ ARRAY_SIZE(ak4671_lout1_mixer_controls)),
+ SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0,
+ &ak4671_rout1_mixer_controls[0],
+ ARRAY_SIZE(ak4671_rout1_mixer_controls)),
+ SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+ 0, 0, &ak4671_lout2_mixer_controls[0],
+ ARRAY_SIZE(ak4671_lout2_mixer_controls),
+ ak4671_out2_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+ 1, 0, &ak4671_rout2_mixer_controls[0],
+ ARRAY_SIZE(ak4671_rout2_mixer_controls),
+ ak4671_out2_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0,
+ &ak4671_lout3_mixer_controls[0],
+ ARRAY_SIZE(ak4671_lout3_mixer_controls)),
+ SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0,
+ &ak4671_rout3_mixer_controls[0],
+ ARRAY_SIZE(ak4671_rout3_mixer_controls)),
+
+ /* Input MUXs */
+ SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0,
+ &ak4671_lin_mux_control),
+ SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0,
+ &ak4671_rin_mux_control),
+
+ /* Mic Power */
+ SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0),
+
+ /* Supply */
+ SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"DAC Left", "NULL", "PMPLL"},
+ {"DAC Right", "NULL", "PMPLL"},
+ {"ADC Left", "NULL", "PMPLL"},
+ {"ADC Right", "NULL", "PMPLL"},
+
+ /* Outputs */
+ {"LOUT1", "NULL", "LOUT1 Mixer"},
+ {"ROUT1", "NULL", "ROUT1 Mixer"},
+ {"LOUT2", "NULL", "LOUT2 Mix Amp"},
+ {"ROUT2", "NULL", "ROUT2 Mix Amp"},
+ {"LOUT3", "NULL", "LOUT3 Mixer"},
+ {"ROUT3", "NULL", "ROUT3 Mixer"},
+
+ {"LOUT1 Mixer", "DACL", "DAC Left"},
+ {"ROUT1 Mixer", "DACR", "DAC Right"},
+ {"LOUT2 Mixer", "DACHL", "DAC Left"},
+ {"ROUT2 Mixer", "DACHR", "DAC Right"},
+ {"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"},
+ {"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"},
+ {"LOUT3 Mixer", "DACSL", "DAC Left"},
+ {"ROUT3 Mixer", "DACSR", "DAC Right"},
+
+ /* Inputs */
+ {"LIN MUX", "LIN1", "LIN1"},
+ {"LIN MUX", "LIN2", "LIN2"},
+ {"LIN MUX", "LIN3", "LIN3"},
+ {"LIN MUX", "LIN4", "LIN4"},
+
+ {"RIN MUX", "RIN1", "RIN1"},
+ {"RIN MUX", "RIN2", "RIN2"},
+ {"RIN MUX", "RIN3", "RIN3"},
+ {"RIN MUX", "RIN4", "RIN4"},
+
+ {"LIN1", NULL, "Mic Bias"},
+ {"RIN1", NULL, "Mic Bias"},
+ {"LIN2", NULL, "Mic Bias"},
+ {"RIN2", NULL, "Mic Bias"},
+
+ {"ADC Left", "NULL", "LIN MUX"},
+ {"ADC Right", "NULL", "RIN MUX"},
+
+ /* Analog Loops */
+ {"LIN1 Mixing Circuit", "NULL", "LIN1"},
+ {"RIN1 Mixing Circuit", "NULL", "RIN1"},
+ {"LIN2 Mixing Circuit", "NULL", "LIN2"},
+ {"RIN2 Mixing Circuit", "NULL", "RIN2"},
+ {"LIN3 Mixing Circuit", "NULL", "LIN3"},
+ {"RIN3 Mixing Circuit", "NULL", "RIN3"},
+ {"LIN4 Mixing Circuit", "NULL", "LIN4"},
+ {"RIN4 Mixing Circuit", "NULL", "RIN4"},
+
+ {"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"},
+
+ {"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"},
+
+ {"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"},
+
+ {"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"},
+ {"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"},
+ {"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"},
+ {"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"},
+ {"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"},
+ {"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
+};
+
+static int ak4671_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, ak4671_dapm_widgets,
+ ARRAY_SIZE(ak4671_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static int ak4671_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;
+ u8 fs;
+
+ fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ fs &= ~AK4671_FS;
+
+ switch (params_rate(params)) {
+ case 8000:
+ fs |= AK4671_FS_8KHZ;
+ break;
+ case 12000:
+ fs |= AK4671_FS_12KHZ;
+ break;
+ case 16000:
+ fs |= AK4671_FS_16KHZ;
+ break;
+ case 24000:
+ fs |= AK4671_FS_24KHZ;
+ break;
+ case 11025:
+ fs |= AK4671_FS_11_025KHZ;
+ break;
+ case 22050:
+ fs |= AK4671_FS_22_05KHZ;
+ break;
+ case 32000:
+ fs |= AK4671_FS_32KHZ;
+ break;
+ case 44100:
+ fs |= AK4671_FS_44_1KHZ;
+ break;
+ case 48000:
+ fs |= AK4671_FS_48KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
+
+ return 0;
+}
+
+static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 pll;
+
+ pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ pll &= ~AK4671_PLL;
+
+ switch (freq) {
+ case 11289600:
+ pll |= AK4671_PLL_11_2896MHZ;
+ break;
+ case 12000000:
+ pll |= AK4671_PLL_12MHZ;
+ break;
+ case 12288000:
+ pll |= AK4671_PLL_12_288MHZ;
+ break;
+ case 13000000:
+ pll |= AK4671_PLL_13MHZ;
+ break;
+ case 13500000:
+ pll |= AK4671_PLL_13_5MHZ;
+ break;
+ case 19200000:
+ pll |= AK4671_PLL_19_2MHZ;
+ break;
+ case 24000000:
+ pll |= AK4671_PLL_24MHZ;
+ break;
+ case 26000000:
+ pll |= AK4671_PLL_26MHZ;
+ break;
+ case 27000000:
+ pll |= AK4671_PLL_27MHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
+
+ return 0;
+}
+
+static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 mode;
+ u8 format;
+
+ /* set master/slave audio interface */
+ mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ mode |= AK4671_M_S;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ mode &= ~(AK4671_M_S);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
+ format &= ~AK4671_DIF;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format |= AK4671_DIF_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format |= AK4671_DIF_MSB_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format |= AK4671_DIF_DSP_MODE;
+ format |= AK4671_BCKP;
+ format |= AK4671_MSBS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set mode and format */
+ snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
+ snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
+
+ return 0;
+}
+
+static int ak4671_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u8 reg;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ case SND_SOC_BIAS_STANDBY:
+ reg = snd_soc_read(codec, AK4671_AD_DA_POWER_MANAGEMENT);
+ snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT,
+ reg | AK4671_PMVCM);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define AK4671_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000)
+
+#define AK4671_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+static struct snd_soc_dai_ops ak4671_dai_ops = {
+ .hw_params = ak4671_hw_params,
+ .set_sysclk = ak4671_set_dai_sysclk,
+ .set_fmt = ak4671_set_dai_fmt,
+};
+
+struct snd_soc_dai ak4671_dai = {
+ .name = "AK4671",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4671_RATES,
+ .formats = AK4671_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4671_RATES,
+ .formats = AK4671_FORMATS,},
+ .ops = &ak4671_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ak4671_dai);
+
+static int ak4671_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (ak4671_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = ak4671_codec;
+ codec = ak4671_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, ak4671_snd_controls,
+ ARRAY_SIZE(ak4671_snd_controls));
+ ak4671_add_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
+ goto card_err;
+ }
+
+ ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+}
+
+static int ak4671_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4671 = {
+ .probe = ak4671_probe,
+ .remove = ak4671_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4671);
+
+static int ak4671_register(struct ak4671_priv *ak4671,
+ enum snd_soc_control_type control)
+{
+ int ret;
+ struct snd_soc_codec *codec = &ak4671->codec;
+
+ if (ak4671_codec) {
+ dev_err(codec->dev, "Another AK4671 is registered\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = ak4671;
+ codec->name = "AK4671";
+ codec->owner = THIS_MODULE;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = ak4671_set_bias_level;
+ codec->dai = &ak4671_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = AK4671_CACHEREGNUM;
+ codec->reg_cache = &ak4671->reg_cache;
+
+ memcpy(codec->reg_cache, ak4671_reg, sizeof(ak4671_reg));
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, control);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ ak4671_dai.dev = codec->dev;
+ ak4671_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dai(&ak4671_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(ak4671);
+ return ret;
+}
+
+static void ak4671_unregister(struct ak4671_priv *ak4671)
+{
+ ak4671_set_bias_level(&ak4671->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dai(&ak4671_dai);
+ snd_soc_unregister_codec(&ak4671->codec);
+ kfree(ak4671);
+ ak4671_codec = NULL;
+}
+
+static int __devinit ak4671_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ak4671_priv *ak4671;
+ struct snd_soc_codec *codec;
+
+ ak4671 = kzalloc(sizeof(struct ak4671_priv), GFP_KERNEL);
+ if (ak4671 == NULL)
+ return -ENOMEM;
+
+ codec = &ak4671->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(client, ak4671);
+ codec->control_data = client;
+
+ codec->dev = &client->dev;
+
+ return ak4671_register(ak4671, SND_SOC_I2C);
+}
+
+static __devexit int ak4671_i2c_remove(struct i2c_client *client)
+{
+ struct ak4671_priv *ak4671 = i2c_get_clientdata(client);
+
+ ak4671_unregister(ak4671);
+
+ return 0;
+}
+
+static const struct i2c_device_id ak4671_i2c_id[] = {
+ { "ak4671", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
+
+static struct i2c_driver ak4671_i2c_driver = {
+ .driver = {
+ .name = "ak4671",
+ .owner = THIS_MODULE,
+ },
+ .probe = ak4671_i2c_probe,
+ .remove = __devexit_p(ak4671_i2c_remove),
+ .id_table = ak4671_i2c_id,
+};
+
+static int __init ak4671_modinit(void)
+{
+ return i2c_add_driver(&ak4671_i2c_driver);
+}
+module_init(ak4671_modinit);
+
+static void __exit ak4671_exit(void)
+{
+ i2c_del_driver(&ak4671_i2c_driver);
+}
+module_exit(ak4671_exit);
+
+MODULE_DESCRIPTION("ASoC AK4671 codec driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
new file mode 100644
index 00000000000..e2fad964e88
--- /dev/null
+++ b/sound/soc/codecs/ak4671.h
@@ -0,0 +1,156 @@
+/*
+ * ak4671.h -- audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef _AK4671_H
+#define _AK4671_H
+
+#define AK4671_AD_DA_POWER_MANAGEMENT 0x00
+#define AK4671_PLL_MODE_SELECT0 0x01
+#define AK4671_PLL_MODE_SELECT1 0x02
+#define AK4671_FORMAT_SELECT 0x03
+#define AK4671_MIC_SIGNAL_SELECT 0x04
+#define AK4671_MIC_AMP_GAIN 0x05
+#define AK4671_MIXING_POWER_MANAGEMENT0 0x06
+#define AK4671_MIXING_POWER_MANAGEMENT1 0x07
+#define AK4671_OUTPUT_VOLUME_CONTROL 0x08
+#define AK4671_LOUT1_SIGNAL_SELECT 0x09
+#define AK4671_ROUT1_SIGNAL_SELECT 0x0a
+#define AK4671_LOUT2_SIGNAL_SELECT 0x0b
+#define AK4671_ROUT2_SIGNAL_SELECT 0x0c
+#define AK4671_LOUT3_SIGNAL_SELECT 0x0d
+#define AK4671_ROUT3_SIGNAL_SELECT 0x0e
+#define AK4671_LOUT1_POWER_MANAGERMENT 0x0f
+#define AK4671_LOUT2_POWER_MANAGERMENT 0x10
+#define AK4671_LOUT3_POWER_MANAGERMENT 0x11
+#define AK4671_LCH_INPUT_VOLUME_CONTROL 0x12
+#define AK4671_RCH_INPUT_VOLUME_CONTROL 0x13
+#define AK4671_ALC_REFERENCE_SELECT 0x14
+#define AK4671_DIGITAL_MIXING_CONTROL 0x15
+#define AK4671_ALC_TIMER_SELECT 0x16
+#define AK4671_ALC_MODE_CONTROL 0x17
+#define AK4671_MODE_CONTROL1 0x18
+#define AK4671_MODE_CONTROL2 0x19
+#define AK4671_LCH_OUTPUT_VOLUME_CONTROL 0x1a
+#define AK4671_RCH_OUTPUT_VOLUME_CONTROL 0x1b
+#define AK4671_SIDETONE_A_CONTROL 0x1c
+#define AK4671_DIGITAL_FILTER_SELECT 0x1d
+#define AK4671_FIL3_COEFFICIENT0 0x1e
+#define AK4671_FIL3_COEFFICIENT1 0x1f
+#define AK4671_FIL3_COEFFICIENT2 0x20
+#define AK4671_FIL3_COEFFICIENT3 0x21
+#define AK4671_EQ_COEFFICIENT0 0x22
+#define AK4671_EQ_COEFFICIENT1 0x23
+#define AK4671_EQ_COEFFICIENT2 0x24
+#define AK4671_EQ_COEFFICIENT3 0x25
+#define AK4671_EQ_COEFFICIENT4 0x26
+#define AK4671_EQ_COEFFICIENT5 0x27
+#define AK4671_FIL1_COEFFICIENT0 0x28
+#define AK4671_FIL1_COEFFICIENT1 0x29
+#define AK4671_FIL1_COEFFICIENT2 0x2a
+#define AK4671_FIL1_COEFFICIENT3 0x2b
+#define AK4671_FIL2_COEFFICIENT0 0x2c
+#define AK4671_FIL2_COEFFICIENT1 0x2d
+#define AK4671_FIL2_COEFFICIENT2 0x2e
+#define AK4671_FIL2_COEFFICIENT3 0x2f
+#define AK4671_DIGITAL_FILTER_SELECT2 0x30
+#define AK4671_E1_COEFFICIENT0 0x32
+#define AK4671_E1_COEFFICIENT1 0x33
+#define AK4671_E1_COEFFICIENT2 0x34
+#define AK4671_E1_COEFFICIENT3 0x35
+#define AK4671_E1_COEFFICIENT4 0x36
+#define AK4671_E1_COEFFICIENT5 0x37
+#define AK4671_E2_COEFFICIENT0 0x38
+#define AK4671_E2_COEFFICIENT1 0x39
+#define AK4671_E2_COEFFICIENT2 0x3a
+#define AK4671_E2_COEFFICIENT3 0x3b
+#define AK4671_E2_COEFFICIENT4 0x3c
+#define AK4671_E2_COEFFICIENT5 0x3d
+#define AK4671_E3_COEFFICIENT0 0x3e
+#define AK4671_E3_COEFFICIENT1 0x3f
+#define AK4671_E3_COEFFICIENT2 0x40
+#define AK4671_E3_COEFFICIENT3 0x41
+#define AK4671_E3_COEFFICIENT4 0x42
+#define AK4671_E3_COEFFICIENT5 0x43
+#define AK4671_E4_COEFFICIENT0 0x44
+#define AK4671_E4_COEFFICIENT1 0x45
+#define AK4671_E4_COEFFICIENT2 0x46
+#define AK4671_E4_COEFFICIENT3 0x47
+#define AK4671_E4_COEFFICIENT4 0x48
+#define AK4671_E4_COEFFICIENT5 0x49
+#define AK4671_E5_COEFFICIENT0 0x4a
+#define AK4671_E5_COEFFICIENT1 0x4b
+#define AK4671_E5_COEFFICIENT2 0x4c
+#define AK4671_E5_COEFFICIENT3 0x4d
+#define AK4671_E5_COEFFICIENT4 0x4e
+#define AK4671_E5_COEFFICIENT5 0x4f
+#define AK4671_EQ_CONTROL_250HZ_100HZ 0x50
+#define AK4671_EQ_CONTROL_3500HZ_1KHZ 0x51
+#define AK4671_EQ_CONTRO_10KHZ 0x52
+#define AK4671_PCM_IF_CONTROL0 0x53
+#define AK4671_PCM_IF_CONTROL1 0x54
+#define AK4671_PCM_IF_CONTROL2 0x55
+#define AK4671_DIGITAL_VOLUME_B_CONTROL 0x56
+#define AK4671_DIGITAL_VOLUME_C_CONTROL 0x57
+#define AK4671_SIDETONE_VOLUME_CONTROL 0x58
+#define AK4671_DIGITAL_MIXING_CONTROL2 0x59
+#define AK4671_SAR_ADC_CONTROL 0x5a
+
+#define AK4671_CACHEREGNUM (AK4671_SAR_ADC_CONTROL + 1)
+
+/* Bitfield Definitions */
+
+/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
+#define AK4671_PMVCM 0x01
+
+/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */
+#define AK4671_PLL 0x0f
+#define AK4671_PLL_11_2896MHZ (4 << 0)
+#define AK4671_PLL_12_288MHZ (5 << 0)
+#define AK4671_PLL_12MHZ (6 << 0)
+#define AK4671_PLL_24MHZ (7 << 0)
+#define AK4671_PLL_19_2MHZ (8 << 0)
+#define AK4671_PLL_13_5MHZ (12 << 0)
+#define AK4671_PLL_27MHZ (13 << 0)
+#define AK4671_PLL_13MHZ (14 << 0)
+#define AK4671_PLL_26MHZ (15 << 0)
+#define AK4671_FS 0xf0
+#define AK4671_FS_8KHZ (0 << 4)
+#define AK4671_FS_12KHZ (1 << 4)
+#define AK4671_FS_16KHZ (2 << 4)
+#define AK4671_FS_24KHZ (3 << 4)
+#define AK4671_FS_11_025KHZ (5 << 4)
+#define AK4671_FS_22_05KHZ (7 << 4)
+#define AK4671_FS_32KHZ (10 << 4)
+#define AK4671_FS_48KHZ (11 << 4)
+#define AK4671_FS_44_1KHZ (15 << 4)
+
+/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */
+#define AK4671_PMPLL 0x01
+#define AK4671_M_S 0x02
+
+/* AK4671_FORMAT_SELECT (0x03) Fields */
+#define AK4671_DIF 0x03
+#define AK4671_DIF_DSP_MODE (0 << 0)
+#define AK4671_DIF_MSB_MODE (2 << 0)
+#define AK4671_DIF_I2S_MODE (3 << 0)
+#define AK4671_BCKP 0x04
+#define AK4671_MSBS 0x08
+#define AK4671_SDOD 0x10
+
+/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */
+#define AK4671_MUTEN 0x04
+
+extern struct snd_soc_dai ak4671_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4671;
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index ca1e24a8f12..565842dcfc6 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -520,6 +520,7 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
+ SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0),
SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1),
@@ -802,22 +803,6 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
* and all registers are written back to the hardware when resuming.
*/
-static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
-{
- struct cs4270_private *cs4270 = i2c_get_clientdata(client);
- struct snd_soc_codec *codec = &cs4270->codec;
-
- return snd_soc_suspend_device(codec->dev);
-}
-
-static int cs4270_i2c_resume(struct i2c_client *client)
-{
- struct cs4270_private *cs4270 = i2c_get_clientdata(client);
- struct snd_soc_codec *codec = &cs4270->codec;
-
- return snd_soc_resume_device(codec->dev);
-}
-
static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
{
struct snd_soc_codec *codec = cs4270_codec;
@@ -853,8 +838,6 @@ static int cs4270_soc_resume(struct platform_device *pdev)
return snd_soc_write(codec, CS4270_PWRCTL, reg);
}
#else
-#define cs4270_i2c_suspend NULL
-#define cs4270_i2c_resume NULL
#define cs4270_soc_suspend NULL
#define cs4270_soc_resume NULL
#endif /* CONFIG_PM */
@@ -873,8 +856,6 @@ static struct i2c_driver cs4270_i2c_driver = {
.id_table = cs4270_id,
.probe = cs4270_i2c_probe,
.remove = cs4270_i2c_remove,
- .suspend = cs4270_i2c_suspend,
- .resume = cs4270_i2c_resume,
};
/*
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
new file mode 100644
index 00000000000..3ca8934fc26
--- /dev/null
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -0,0 +1,1237 @@
+/*
+ * ALSA SoC Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * 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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.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 <sound/tlv320dac33-plat.h>
+#include "tlv320dac33.h"
+
+#define DAC33_BUFFER_SIZE_BYTES 24576 /* bytes, 12288 16 bit words,
+ * 6144 stereo */
+#define DAC33_BUFFER_SIZE_SAMPLES 6144
+
+#define NSAMPLE_MAX 5700
+
+#define LATENCY_TIME_MS 20
+
+static struct snd_soc_codec *tlv320dac33_codec;
+
+enum dac33_state {
+ DAC33_IDLE = 0,
+ DAC33_PREFILL,
+ DAC33_PLAYBACK,
+ DAC33_FLUSH,
+};
+
+struct tlv320dac33_priv {
+ struct mutex mutex;
+ struct workqueue_struct *dac33_wq;
+ struct work_struct work;
+ struct snd_soc_codec codec;
+ int power_gpio;
+ int chip_power;
+ int irq;
+ unsigned int refclk;
+
+ unsigned int alarm_threshold; /* set to be half of LATENCY_TIME_MS */
+ unsigned int nsample_min; /* nsample should not be lower than
+ * this */
+ unsigned int nsample_max; /* nsample should not be higher than
+ * this */
+ unsigned int nsample_switch; /* Use FIFO or bypass FIFO switch */
+ unsigned int nsample; /* burst read amount from host */
+
+ enum dac33_state state;
+};
+
+static const u8 dac33_reg[DAC33_CACHEREGNUM] = {
+0x00, 0x00, 0x00, 0x00, /* 0x00 - 0x03 */
+0x00, 0x00, 0x00, 0x00, /* 0x04 - 0x07 */
+0x00, 0x00, 0x00, 0x00, /* 0x08 - 0x0b */
+0x00, 0x00, 0x00, 0x00, /* 0x0c - 0x0f */
+0x00, 0x00, 0x00, 0x00, /* 0x10 - 0x13 */
+0x00, 0x00, 0x00, 0x00, /* 0x14 - 0x17 */
+0x00, 0x00, 0x00, 0x00, /* 0x18 - 0x1b */
+0x00, 0x00, 0x00, 0x00, /* 0x1c - 0x1f */
+0x00, 0x00, 0x00, 0x00, /* 0x20 - 0x23 */
+0x00, 0x00, 0x00, 0x00, /* 0x24 - 0x27 */
+0x00, 0x00, 0x00, 0x00, /* 0x28 - 0x2b */
+0x00, 0x00, 0x00, 0x80, /* 0x2c - 0x2f */
+0x80, 0x00, 0x00, 0x00, /* 0x30 - 0x33 */
+0x00, 0x00, 0x00, 0x00, /* 0x34 - 0x37 */
+0x00, 0x00, /* 0x38 - 0x39 */
+/* Registers 0x3a - 0x3f are reserved */
+ 0x00, 0x00, /* 0x3a - 0x3b */
+0x00, 0x00, 0x00, 0x00, /* 0x3c - 0x3f */
+
+0x00, 0x00, 0x00, 0x00, /* 0x40 - 0x43 */
+0x00, 0x80, /* 0x44 - 0x45 */
+/* Registers 0x46 - 0x47 are reserved */
+ 0x80, 0x80, /* 0x46 - 0x47 */
+
+0x80, 0x00, 0x00, /* 0x48 - 0x4a */
+/* Registers 0x4b - 0x7c are reserved */
+ 0x00, /* 0x4b */
+0x00, 0x00, 0x00, 0x00, /* 0x4c - 0x4f */
+0x00, 0x00, 0x00, 0x00, /* 0x50 - 0x53 */
+0x00, 0x00, 0x00, 0x00, /* 0x54 - 0x57 */
+0x00, 0x00, 0x00, 0x00, /* 0x58 - 0x5b */
+0x00, 0x00, 0x00, 0x00, /* 0x5c - 0x5f */
+0x00, 0x00, 0x00, 0x00, /* 0x60 - 0x63 */
+0x00, 0x00, 0x00, 0x00, /* 0x64 - 0x67 */
+0x00, 0x00, 0x00, 0x00, /* 0x68 - 0x6b */
+0x00, 0x00, 0x00, 0x00, /* 0x6c - 0x6f */
+0x00, 0x00, 0x00, 0x00, /* 0x70 - 0x73 */
+0x00, 0x00, 0x00, 0x00, /* 0x74 - 0x77 */
+0x00, 0x00, 0x00, 0x00, /* 0x78 - 0x7b */
+0x00, /* 0x7c */
+
+ 0xda, 0x33, 0x03, /* 0x7d - 0x7f */
+};
+
+/* Register read and write */
+static inline unsigned int dac33_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned reg)
+{
+ u8 *cache = codec->reg_cache;
+ if (reg >= DAC33_CACHEREGNUM)
+ return 0;
+
+ return cache[reg];
+}
+
+static inline void dac33_write_reg_cache(struct snd_soc_codec *codec,
+ u8 reg, u8 value)
+{
+ u8 *cache = codec->reg_cache;
+ if (reg >= DAC33_CACHEREGNUM)
+ return;
+
+ cache[reg] = value;
+}
+
+static int dac33_read(struct snd_soc_codec *codec, unsigned int reg,
+ u8 *value)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ int val;
+
+ *value = reg & 0xff;
+
+ /* If powered off, return the cached value */
+ if (dac33->chip_power) {
+ val = i2c_smbus_read_byte_data(codec->control_data, value[0]);
+ if (val < 0) {
+ dev_err(codec->dev, "Read failed (%d)\n", val);
+ value[0] = dac33_read_reg_cache(codec, reg);
+ } else {
+ value[0] = val;
+ dac33_write_reg_cache(codec, reg, val);
+ }
+ } else {
+ value[0] = dac33_read_reg_cache(codec, reg);
+ }
+
+ return 0;
+}
+
+static int dac33_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ u8 data[2];
+ int ret = 0;
+
+ /*
+ * data is
+ * D15..D8 dac33 register offset
+ * D7...D0 register data
+ */
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ dac33_write_reg_cache(codec, data[0], data[1]);
+ if (dac33->chip_power) {
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret != 2)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int dac33_write_locked(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ int ret;
+
+ mutex_lock(&dac33->mutex);
+ ret = dac33_write(codec, reg, value);
+ mutex_unlock(&dac33->mutex);
+
+ return ret;
+}
+
+#define DAC33_I2C_ADDR_AUTOINC 0x80
+static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ u8 data[3];
+ int ret = 0;
+
+ /*
+ * data is
+ * D23..D16 dac33 register offset
+ * D15..D8 register data MSB
+ * D7...D0 register data LSB
+ */
+ data[0] = reg & 0xff;
+ data[1] = (value >> 8) & 0xff;
+ data[2] = value & 0xff;
+
+ dac33_write_reg_cache(codec, data[0], data[1]);
+ dac33_write_reg_cache(codec, data[0] + 1, data[2]);
+
+ if (dac33->chip_power) {
+ /* We need to set autoincrement mode for 16 bit writes */
+ data[0] |= DAC33_I2C_ADDR_AUTOINC;
+ ret = codec->hw_write(codec->control_data, data, 3);
+ if (ret != 3)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void dac33_restore_regs(struct snd_soc_codec *codec)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ u8 *cache = codec->reg_cache;
+ u8 data[2];
+ int i, ret;
+
+ if (!dac33->chip_power)
+ return;
+
+ for (i = DAC33_PWR_CTRL; i <= DAC33_INTP_CTRL_B; i++) {
+ data[0] = i;
+ data[1] = cache[i];
+ /* Skip the read only registers */
+ if ((i >= DAC33_INT_OSC_STATUS &&
+ i <= DAC33_INT_OSC_FREQ_RAT_READ_B) ||
+ (i >= DAC33_FIFO_WPTR_MSB && i <= DAC33_FIFO_IRQ_FLAG) ||
+ i == DAC33_DAC_STATUS_FLAGS ||
+ i == DAC33_SRC_EST_REF_CLK_RATIO_A ||
+ i == DAC33_SRC_EST_REF_CLK_RATIO_B)
+ continue;
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret != 2)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ }
+ for (i = DAC33_LDAC_PWR_CTRL; i <= DAC33_LINEL_TO_LLO_VOL; i++) {
+ data[0] = i;
+ data[1] = cache[i];
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret != 2)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ }
+ for (i = DAC33_LINER_TO_RLO_VOL; i <= DAC33_OSC_TRIM; i++) {
+ data[0] = i;
+ data[1] = cache[i];
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret != 2)
+ dev_err(codec->dev, "Write failed (%d)\n", ret);
+ }
+}
+
+static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
+{
+ u8 reg;
+
+ reg = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ if (power)
+ reg |= DAC33_PDNALLB;
+ else
+ reg &= ~DAC33_PDNALLB;
+ dac33_write(codec, DAC33_PWR_CTRL, reg);
+}
+
+static void dac33_hard_power(struct snd_soc_codec *codec, int power)
+{
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+
+ mutex_lock(&dac33->mutex);
+ if (power) {
+ if (dac33->power_gpio >= 0) {
+ gpio_set_value(dac33->power_gpio, 1);
+ dac33->chip_power = 1;
+ /* Restore registers */
+ dac33_restore_regs(codec);
+ }
+ dac33_soft_power(codec, 1);
+ } else {
+ dac33_soft_power(codec, 0);
+ if (dac33->power_gpio >= 0) {
+ gpio_set_value(dac33->power_gpio, 0);
+ dac33->chip_power = 0;
+ }
+ }
+ mutex_unlock(&dac33->mutex);
+
+}
+
+static int dac33_get_nsample(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+
+ ucontrol->value.integer.value[0] = dac33->nsample;
+
+ return 0;
+}
+
+static int dac33_set_nsample(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ int ret = 0;
+
+ if (dac33->nsample == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (ucontrol->value.integer.value[0] < dac33->nsample_min ||
+ ucontrol->value.integer.value[0] > dac33->nsample_max)
+ ret = -EINVAL;
+ else
+ dac33->nsample = ucontrol->value.integer.value[0];
+
+ return ret;
+}
+
+static int dac33_get_nsample_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+
+ ucontrol->value.integer.value[0] = dac33->nsample_switch;
+
+ return 0;
+}
+
+static int dac33_set_nsample_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ int ret = 0;
+
+ if (dac33->nsample_switch == ucontrol->value.integer.value[0])
+ return 0;
+ /* Do not allow changes while stream is running*/
+ if (codec->active)
+ return -EPERM;
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] > 1)
+ ret = -EINVAL;
+ else
+ dac33->nsample_switch = ucontrol->value.integer.value[0];
+
+ return ret;
+}
+
+/*
+ * DACL/R digital volume control:
+ * from 0 dB to -63.5 in 0.5 dB steps
+ * Need to be inverted later on:
+ * 0x00 == 0 dB
+ * 0x7f == -63.5 dB
+ */
+static DECLARE_TLV_DB_SCALE(dac_digivol_tlv, -6350, 50, 0);
+
+static const struct snd_kcontrol_new dac33_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC Digital Playback Volume",
+ DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL,
+ 0, 0x7f, 1, dac_digivol_tlv),
+ SOC_DOUBLE_R("DAC Digital Playback Switch",
+ DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL, 7, 1, 1),
+ SOC_DOUBLE_R("Line to Line Out Volume",
+ DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1),
+};
+
+static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = {
+ SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
+ dac33_get_nsample, dac33_set_nsample),
+ SOC_SINGLE_EXT("nSample Switch", 0, 0, 1, 0,
+ dac33_get_nsample_switch, dac33_set_nsample_switch),
+};
+
+/* Analog bypass */
+static const struct snd_kcontrol_new dac33_dapm_abypassl_control =
+ SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1);
+
+static const struct snd_kcontrol_new dac33_dapm_abypassr_control =
+ SOC_DAPM_SINGLE("Switch", DAC33_LINER_TO_RLO_VOL, 7, 1, 1);
+
+static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("LEFT_LO"),
+ SND_SOC_DAPM_OUTPUT("RIGHT_LO"),
+
+ SND_SOC_DAPM_INPUT("LINEL"),
+ SND_SOC_DAPM_INPUT("LINER"),
+
+ SND_SOC_DAPM_DAC("DACL", "Left Playback", DAC33_LDAC_PWR_CTRL, 2, 0),
+ SND_SOC_DAPM_DAC("DACR", "Right Playback", DAC33_RDAC_PWR_CTRL, 2, 0),
+
+ /* Analog bypass */
+ SND_SOC_DAPM_SWITCH("Analog Left Bypass", SND_SOC_NOPM, 0, 0,
+ &dac33_dapm_abypassl_control),
+ SND_SOC_DAPM_SWITCH("Analog Right Bypass", SND_SOC_NOPM, 0, 0,
+ &dac33_dapm_abypassr_control),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Left Amp Power",
+ DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amp Power",
+ DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Analog bypass */
+ {"Analog Left Bypass", "Switch", "LINEL"},
+ {"Analog Right Bypass", "Switch", "LINER"},
+
+ {"Output Left Amp Power", NULL, "DACL"},
+ {"Output Right Amp Power", NULL, "DACR"},
+
+ {"Output Left Amp Power", NULL, "Analog Left Bypass"},
+ {"Output Right Amp Power", NULL, "Analog Right Bypass"},
+
+ /* output */
+ {"LEFT_LO", NULL, "Output Left Amp Power"},
+ {"RIGHT_LO", NULL, "Output Right Amp Power"},
+};
+
+static int dac33_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, dac33_dapm_widgets,
+ ARRAY_SIZE(dac33_dapm_widgets));
+
+ /* set up audio path interconnects */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ snd_soc_dapm_new_widgets(codec);
+
+ return 0;
+}
+
+static int dac33_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ dac33_soft_power(codec, 1);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF)
+ dac33_hard_power(codec, 1);
+ dac33_soft_power(codec, 0);
+ break;
+ case SND_SOC_BIAS_OFF:
+ dac33_hard_power(codec, 0);
+ break;
+ }
+ codec->bias_level = level;
+
+ return 0;
+}
+
+static void dac33_work(struct work_struct *work)
+{
+ struct snd_soc_codec *codec;
+ struct tlv320dac33_priv *dac33;
+ u8 reg;
+
+ dac33 = container_of(work, struct tlv320dac33_priv, work);
+ codec = &dac33->codec;
+
+ mutex_lock(&dac33->mutex);
+ switch (dac33->state) {
+ case DAC33_PREFILL:
+ dac33->state = DAC33_PLAYBACK;
+ dac33_write16(codec, DAC33_NSAMPLE_MSB,
+ DAC33_THRREG(dac33->nsample));
+ dac33_write16(codec, DAC33_PREFILL_MSB,
+ DAC33_THRREG(dac33->alarm_threshold));
+ break;
+ case DAC33_PLAYBACK:
+ dac33_write16(codec, DAC33_NSAMPLE_MSB,
+ DAC33_THRREG(dac33->nsample));
+ break;
+ case DAC33_IDLE:
+ break;
+ case DAC33_FLUSH:
+ dac33->state = DAC33_IDLE;
+ /* Mask all interrupts from dac33 */
+ dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0);
+
+ /* flush fifo */
+ reg = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+ reg |= DAC33_FIFOFLUSH;
+ dac33_write(codec, DAC33_FIFO_CTRL_A, reg);
+ break;
+ }
+ mutex_unlock(&dac33->mutex);
+}
+
+static irqreturn_t dac33_interrupt_handler(int irq, void *dev)
+{
+ struct snd_soc_codec *codec = dev;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+
+ queue_work(dac33->dac33_wq, &dac33->work);
+
+ return IRQ_HANDLED;
+}
+
+static void dac33_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ unsigned int pwr_ctrl;
+
+ /* Stop pending workqueue */
+ if (dac33->nsample_switch)
+ cancel_work_sync(&dac33->work);
+
+ mutex_lock(&dac33->mutex);
+ pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ pwr_ctrl &= ~(DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB);
+ dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
+ mutex_unlock(&dac33->mutex);
+}
+
+static void dac33_oscwait(struct snd_soc_codec *codec)
+{
+ int timeout = 20;
+ u8 reg;
+
+ do {
+ msleep(1);
+ dac33_read(codec, DAC33_INT_OSC_STATUS, &reg);
+ } while (((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) && timeout--);
+ if ((reg & 0x03) != DAC33_OSCSTATUS_NORMAL)
+ dev_err(codec->dev,
+ "internal oscillator calibration failed\n");
+}
+
+static int dac33_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ /* Check parameters for validity */
+ switch (params_rate(params)) {
+ case 44100:
+ case 48000:
+ break;
+ default:
+ dev_err(codec->dev, "unsupported rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ default:
+ dev_err(codec->dev, "unsupported format %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define CALC_OSCSET(rate, refclk) ( \
+ ((((rate * 10000) / refclk) * 4096) + 5000) / 10000)
+#define CALC_RATIOSET(rate, refclk) ( \
+ ((((refclk * 100000) / rate) * 16384) + 50000) / 100000)
+
+/*
+ * tlv320dac33 is strict on the sequence of the register writes, if the register
+ * writes happens in different order, than dac33 might end up in unknown state.
+ * Use the known, working sequence of register writes to initialize the dac33.
+ */
+static int dac33_prepare_chip(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
+ u8 aictrl_a, fifoctrl_a;
+
+ switch (substream->runtime->rate) {
+ case 44100:
+ case 48000:
+ oscset = CALC_OSCSET(substream->runtime->rate, dac33->refclk);
+ ratioset = CALC_RATIOSET(substream->runtime->rate,
+ dac33->refclk);
+ break;
+ default:
+ dev_err(codec->dev, "unsupported rate %d\n",
+ substream->runtime->rate);
+ return -EINVAL;
+ }
+
+
+ aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
+ aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK);
+ fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+ fifoctrl_a &= ~DAC33_WIDTH;
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ aictrl_a |= (DAC33_NCYCL_16 | DAC33_WLEN_16);
+ fifoctrl_a |= DAC33_WIDTH;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported format %d\n",
+ substream->runtime->format);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dac33->mutex);
+ dac33_soft_power(codec, 1);
+
+ reg_tmp = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
+ dac33_write(codec, DAC33_INT_OSC_CTRL, reg_tmp);
+
+ /* Write registers 0x08 and 0x09 (MSB, LSB) */
+ dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
+
+ /* calib time: 128 is a nice number ;) */
+ dac33_write(codec, DAC33_CALIB_TIME, 128);
+
+ /* adjustment treshold & step */
+ dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
+ DAC33_ADJSTEP(1));
+
+ /* div=4 / gain=1 / div */
+ dac33_write(codec, DAC33_INT_OSC_CTRL_C, DAC33_REFDIV(4));
+
+ pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+ pwr_ctrl |= DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB;
+ dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
+
+ dac33_oscwait(codec);
+
+ if (dac33->nsample_switch) {
+ /* 50-51 : ASRC Control registers */
+ dac33_write(codec, DAC33_ASRC_CTRL_A, (1 << 4)); /* div=2 */
+ dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */
+
+ /* Write registers 0x34 and 0x35 (MSB, LSB) */
+ dac33_write16(codec, DAC33_SRC_REF_CLK_RATIO_A, ratioset);
+
+ /* Set interrupts to high active */
+ dac33_write(codec, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH);
+
+ dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
+ DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL));
+ dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
+ } else {
+ /* 50-51 : ASRC Control registers */
+ dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCBYP);
+ dac33_write(codec, DAC33_ASRC_CTRL_B, 0); /* ??? */
+ }
+
+ if (dac33->nsample_switch)
+ fifoctrl_a &= ~DAC33_FBYPAS;
+ else
+ fifoctrl_a |= DAC33_FBYPAS;
+ dac33_write(codec, DAC33_FIFO_CTRL_A, fifoctrl_a);
+
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+ reg_tmp = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+ if (dac33->nsample_switch)
+ reg_tmp &= ~DAC33_BCLKON;
+ else
+ reg_tmp |= DAC33_BCLKON;
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg_tmp);
+
+ if (dac33->nsample_switch) {
+ /* 20: BCLK divide ratio */
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 3);
+
+ dac33_write16(codec, DAC33_ATHR_MSB,
+ DAC33_THRREG(dac33->alarm_threshold));
+ } else {
+ dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
+ }
+
+ mutex_unlock(&dac33->mutex);
+
+ return 0;
+}
+
+static void dac33_calculate_times(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ unsigned int nsample_limit;
+
+ /* Number of samples (16bit, stereo) in one period */
+ dac33->nsample_min = snd_pcm_lib_period_bytes(substream) / 4;
+
+ /* Number of samples (16bit, stereo) in ALSA buffer */
+ dac33->nsample_max = snd_pcm_lib_buffer_bytes(substream) / 4;
+ /* Subtract one period from the total */
+ dac33->nsample_max -= dac33->nsample_min;
+
+ /* Number of samples for LATENCY_TIME_MS / 2 */
+ dac33->alarm_threshold = substream->runtime->rate /
+ (1000 / (LATENCY_TIME_MS / 2));
+
+ /* Find and fix up the lowest nsmaple limit */
+ nsample_limit = substream->runtime->rate / (1000 / LATENCY_TIME_MS);
+
+ if (dac33->nsample_min < nsample_limit)
+ dac33->nsample_min = nsample_limit;
+
+ if (dac33->nsample < dac33->nsample_min)
+ dac33->nsample = dac33->nsample_min;
+
+ /*
+ * Find and fix up the highest nsmaple limit
+ * In order to not overflow the DAC33 buffer substract the
+ * alarm_threshold value from the size of the DAC33 buffer
+ */
+ nsample_limit = DAC33_BUFFER_SIZE_SAMPLES - dac33->alarm_threshold;
+
+ if (dac33->nsample_max > nsample_limit)
+ dac33->nsample_max = nsample_limit;
+
+ if (dac33->nsample > dac33->nsample_max)
+ dac33->nsample = dac33->nsample_max;
+}
+
+static int dac33_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ dac33_calculate_times(substream);
+ dac33_prepare_chip(substream);
+
+ return 0;
+}
+
+static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (dac33->nsample_switch) {
+ dac33->state = DAC33_PREFILL;
+ queue_work(dac33->dac33_wq, &dac33->work);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (dac33->nsample_switch) {
+ dac33->state = DAC33_FLUSH;
+ queue_work(dac33->dac33_wq, &dac33->work);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int dac33_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct tlv320dac33_priv *dac33 = codec->private_data;
+ u8 ioc_reg, asrcb_reg;
+
+ ioc_reg = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
+ asrcb_reg = dac33_read_reg_cache(codec, DAC33_ASRC_CTRL_B);
+ switch (clk_id) {
+ case TLV320DAC33_MCLK:
+ ioc_reg |= DAC33_REFSEL;
+ asrcb_reg |= DAC33_SRCREFSEL;
+ break;
+ case TLV320DAC33_SLEEPCLK:
+ ioc_reg &= ~DAC33_REFSEL;
+ asrcb_reg &= ~DAC33_SRCREFSEL;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock ID (%d)\n", clk_id);
+ break;
+ }
+ dac33->refclk = freq;
+
+ dac33_write_reg_cache(codec, DAC33_INT_OSC_CTRL, ioc_reg);
+ dac33_write_reg_cache(codec, DAC33_ASRC_CTRL_B, asrcb_reg);
+
+ return 0;
+}
+
+static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 aictrl_a, aictrl_b;
+
+ aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
+ aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Codec Master */
+ aictrl_a |= (DAC33_MSBCLK | DAC33_MSWCLK);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Codec Slave */
+ aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ aictrl_a &= ~DAC33_AFMT_MASK;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aictrl_a |= DAC33_AFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ aictrl_a |= DAC33_AFMT_DSP;
+ aictrl_b &= ~DAC33_DATA_DELAY_MASK;
+ aictrl_b |= DAC33_DATA_DELAY(1); /* 1 bit delay */
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aictrl_a |= DAC33_AFMT_DSP;
+ aictrl_b &= ~DAC33_DATA_DELAY_MASK; /* No delay */
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ aictrl_a |= DAC33_AFMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aictrl_a |= DAC33_AFMT_LEFT_J;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported format (%u)\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+ dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
+
+ return 0;
+}
+
+static void dac33_init_chip(struct snd_soc_codec *codec)
+{
+ /* 44-46: DAC Control Registers */
+ /* A : DAC sample rate Fsref/1.5 */
+ dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(1));
+ /* B : DAC src=normal, not muted */
+ dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
+ DAC33_DACSRCL_LEFT);
+ /* C : (defaults) */
+ dac33_write(codec, DAC33_DAC_CTRL_C, 0x00);
+
+ /* 64-65 : L&R DAC power control
+ Line In -> OUT 1V/V Gain, DAC -> OUT 4V/V Gain*/
+ dac33_write(codec, DAC33_LDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
+ dac33_write(codec, DAC33_RDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
+
+ /* 73 : volume soft stepping control,
+ clock source = internal osc (?) */
+ dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN);
+
+ /* 66 : LOP/LOM Modes */
+ dac33_write(codec, DAC33_OUT_AMP_CM_CTRL, 0xff);
+
+ /* 68 : LOM inverted from LOP */
+ dac33_write(codec, DAC33_OUT_AMP_CTRL, (3<<2));
+
+ dac33_write(codec, DAC33_PWR_CTRL, DAC33_PDNALLB);
+}
+
+static int dac33_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct tlv320dac33_priv *dac33;
+ int ret = 0;
+
+ BUG_ON(!tlv320dac33_codec);
+
+ codec = tlv320dac33_codec;
+ socdev->card->codec = codec;
+ dac33 = codec->private_data;
+
+ /* Power up the codec */
+ dac33_hard_power(codec, 1);
+ /* Set default configuration */
+ dac33_init_chip(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, dac33_snd_controls,
+ ARRAY_SIZE(dac33_snd_controls));
+ /* Only add the nSample controls, if we have valid IRQ number */
+ if (dac33->irq >= 0)
+ snd_soc_add_controls(codec, dac33_nsample_snd_controls,
+ ARRAY_SIZE(dac33_nsample_snd_controls));
+
+ dac33_add_widgets(codec);
+
+ /* power on device */
+ dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card\n");
+ goto card_err;
+ }
+
+ return 0;
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ dac33_hard_power(codec, 0);
+ return ret;
+}
+
+static int dac33_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+static int dac33_soc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int dac33_soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ dac33_set_bias_level(codec, codec->suspend_bias_level);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_tlv320dac33 = {
+ .probe = dac33_soc_probe,
+ .remove = dac33_soc_remove,
+ .suspend = dac33_soc_suspend,
+ .resume = dac33_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320dac33);
+
+#define DAC33_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+#define DAC33_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+static struct snd_soc_dai_ops dac33_dai_ops = {
+ .shutdown = dac33_shutdown,
+ .hw_params = dac33_hw_params,
+ .prepare = dac33_pcm_prepare,
+ .trigger = dac33_pcm_trigger,
+ .set_sysclk = dac33_set_dai_sysclk,
+ .set_fmt = dac33_set_dai_fmt,
+};
+
+struct snd_soc_dai dac33_dai = {
+ .name = "tlv320dac33",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAC33_RATES,
+ .formats = DAC33_FORMATS,},
+ .ops = &dac33_dai_ops,
+};
+EXPORT_SYMBOL_GPL(dac33_dai);
+
+static int dac33_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tlv320dac33_platform_data *pdata;
+ struct tlv320dac33_priv *dac33;
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (client->dev.platform_data == NULL) {
+ dev_err(&client->dev, "Platform data not set\n");
+ return -ENODEV;
+ }
+ pdata = client->dev.platform_data;
+
+ dac33 = kzalloc(sizeof(struct tlv320dac33_priv), GFP_KERNEL);
+ if (dac33 == NULL)
+ return -ENOMEM;
+
+ codec = &dac33->codec;
+ codec->private_data = dac33;
+ codec->control_data = client;
+
+ mutex_init(&codec->mutex);
+ mutex_init(&dac33->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->name = "tlv320dac33";
+ codec->owner = THIS_MODULE;
+ codec->read = dac33_read_reg_cache;
+ codec->write = dac33_write_locked;
+ codec->hw_write = (hw_write_t) i2c_master_send;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = dac33_set_bias_level;
+ codec->dai = &dac33_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(dac33_reg);
+ codec->reg_cache = kmemdup(dac33_reg, ARRAY_SIZE(dac33_reg),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL) {
+ ret = -ENOMEM;
+ goto error_reg;
+ }
+
+ i2c_set_clientdata(client, dac33);
+
+ dac33->power_gpio = pdata->power_gpio;
+ dac33->irq = client->irq;
+ dac33->nsample = NSAMPLE_MAX;
+ /* Disable FIFO use by default */
+ dac33->nsample_switch = 0;
+
+ tlv320dac33_codec = codec;
+
+ codec->dev = &client->dev;
+ dac33_dai.dev = codec->dev;
+
+ /* Check if the reset GPIO number is valid and request it */
+ if (dac33->power_gpio >= 0) {
+ ret = gpio_request(dac33->power_gpio, "tlv320dac33 reset");
+ if (ret < 0) {
+ dev_err(codec->dev,
+ "Failed to request reset GPIO (%d)\n",
+ dac33->power_gpio);
+ snd_soc_unregister_dai(&dac33_dai);
+ snd_soc_unregister_codec(codec);
+ goto error_gpio;
+ }
+ gpio_direction_output(dac33->power_gpio, 0);
+ } else {
+ dac33->chip_power = 1;
+ }
+
+ /* Check if the IRQ number is valid and request it */
+ if (dac33->irq >= 0) {
+ ret = request_irq(dac33->irq, dac33_interrupt_handler,
+ IRQF_TRIGGER_RISING | IRQF_DISABLED,
+ codec->name, codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Could not request IRQ%d (%d)\n",
+ dac33->irq, ret);
+ dac33->irq = -1;
+ }
+ if (dac33->irq != -1) {
+ /* Setup work queue */
+ dac33->dac33_wq = create_rt_workqueue("tlv320dac33");
+ if (dac33->dac33_wq == NULL) {
+ free_irq(dac33->irq, &dac33->codec);
+ ret = -ENOMEM;
+ goto error_wq;
+ }
+
+ INIT_WORK(&dac33->work, dac33_work);
+ }
+ }
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto error_codec;
+ }
+
+ ret = snd_soc_register_dai(&dac33_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ goto error_codec;
+ }
+
+ /* Shut down the codec for now */
+ dac33_hard_power(codec, 0);
+
+ return ret;
+
+error_codec:
+ if (dac33->irq >= 0) {
+ free_irq(dac33->irq, &dac33->codec);
+ destroy_workqueue(dac33->dac33_wq);
+ }
+error_wq:
+ if (dac33->power_gpio >= 0)
+ gpio_free(dac33->power_gpio);
+error_gpio:
+ kfree(codec->reg_cache);
+error_reg:
+ tlv320dac33_codec = NULL;
+ kfree(dac33);
+
+ return ret;
+}
+
+static int dac33_i2c_remove(struct i2c_client *client)
+{
+ struct tlv320dac33_priv *dac33;
+
+ dac33 = i2c_get_clientdata(client);
+ dac33_hard_power(&dac33->codec, 0);
+
+ if (dac33->power_gpio >= 0)
+ gpio_free(dac33->power_gpio);
+ if (dac33->irq >= 0)
+ free_irq(dac33->irq, &dac33->codec);
+
+ destroy_workqueue(dac33->dac33_wq);
+ snd_soc_unregister_dai(&dac33_dai);
+ snd_soc_unregister_codec(&dac33->codec);
+ kfree(dac33->codec.reg_cache);
+ kfree(dac33);
+ tlv320dac33_codec = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id tlv320dac33_i2c_id[] = {
+ {
+ .name = "tlv320dac33",
+ .driver_data = 0,
+ },
+ { },
+};
+
+static struct i2c_driver tlv320dac33_i2c_driver = {
+ .driver = {
+ .name = "tlv320dac33",
+ .owner = THIS_MODULE,
+ },
+ .probe = dac33_i2c_probe,
+ .remove = __devexit_p(dac33_i2c_remove),
+ .id_table = tlv320dac33_i2c_id,
+};
+
+static int __init dac33_module_init(void)
+{
+ int r;
+ r = i2c_add_driver(&tlv320dac33_i2c_driver);
+ if (r < 0) {
+ printk(KERN_ERR "DAC33: driver registration failed\n");
+ return r;
+ }
+ return 0;
+}
+module_init(dac33_module_init);
+
+static void __exit dac33_module_exit(void)
+{
+ i2c_del_driver(&tlv320dac33_i2c_driver);
+}
+module_exit(dac33_module_exit);
+
+
+MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h
new file mode 100644
index 00000000000..eb8ae07f0bd
--- /dev/null
+++ b/sound/soc/codecs/tlv320dac33.h
@@ -0,0 +1,267 @@
+/*
+ * ALSA SoC Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * 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
+ *
+ */
+
+#ifndef __TLV320DAC33_H
+#define __TLV320DAC33_H
+
+#define DAC33_PAGE_SELECT 0x00
+#define DAC33_PWR_CTRL 0x01
+#define DAC33_PLL_CTRL_A 0x02
+#define DAC33_PLL_CTRL_B 0x03
+#define DAC33_PLL_CTRL_C 0x04
+#define DAC33_PLL_CTRL_D 0x05
+#define DAC33_PLL_CTRL_E 0x06
+#define DAC33_INT_OSC_CTRL 0x07
+#define DAC33_INT_OSC_FREQ_RAT_A 0x08
+#define DAC33_INT_OSC_FREQ_RAT_B 0x09
+#define DAC33_INT_OSC_DAC_RATIO_SET 0x0A
+#define DAC33_CALIB_TIME 0x0B
+#define DAC33_INT_OSC_CTRL_B 0x0C
+#define DAC33_INT_OSC_CTRL_C 0x0D
+#define DAC33_INT_OSC_STATUS 0x0E
+#define DAC33_INT_OSC_DAC_RATIO_READ 0x0F
+#define DAC33_INT_OSC_FREQ_RAT_READ_A 0x10
+#define DAC33_INT_OSC_FREQ_RAT_READ_B 0x11
+#define DAC33_SER_AUDIOIF_CTRL_A 0x12
+#define DAC33_SER_AUDIOIF_CTRL_B 0x13
+#define DAC33_SER_AUDIOIF_CTRL_C 0x14
+#define DAC33_FIFO_CTRL_A 0x15
+#define DAC33_UTHR_MSB 0x16
+#define DAC33_UTHR_LSB 0x17
+#define DAC33_ATHR_MSB 0x18
+#define DAC33_ATHR_LSB 0x19
+#define DAC33_LTHR_MSB 0x1A
+#define DAC33_LTHR_LSB 0x1B
+#define DAC33_PREFILL_MSB 0x1C
+#define DAC33_PREFILL_LSB 0x1D
+#define DAC33_NSAMPLE_MSB 0x1E
+#define DAC33_NSAMPLE_LSB 0x1F
+#define DAC33_FIFO_WPTR_MSB 0x20
+#define DAC33_FIFO_WPTR_LSB 0x21
+#define DAC33_FIFO_RPTR_MSB 0x22
+#define DAC33_FIFO_RPTR_LSB 0x23
+#define DAC33_FIFO_DEPTH_MSB 0x24
+#define DAC33_FIFO_DEPTH_LSB 0x25
+#define DAC33_SAMPLES_REMAINING_MSB 0x26
+#define DAC33_SAMPLES_REMAINING_LSB 0x27
+#define DAC33_FIFO_IRQ_FLAG 0x28
+#define DAC33_FIFO_IRQ_MASK 0x29
+#define DAC33_FIFO_IRQ_MODE_A 0x2A
+#define DAC33_FIFO_IRQ_MODE_B 0x2B
+#define DAC33_DAC_CTRL_A 0x2C
+#define DAC33_DAC_CTRL_B 0x2D
+#define DAC33_DAC_CTRL_C 0x2E
+#define DAC33_LDAC_DIG_VOL_CTRL 0x2F
+#define DAC33_RDAC_DIG_VOL_CTRL 0x30
+#define DAC33_DAC_STATUS_FLAGS 0x31
+#define DAC33_ASRC_CTRL_A 0x32
+#define DAC33_ASRC_CTRL_B 0x33
+#define DAC33_SRC_REF_CLK_RATIO_A 0x34
+#define DAC33_SRC_REF_CLK_RATIO_B 0x35
+#define DAC33_SRC_EST_REF_CLK_RATIO_A 0x36
+#define DAC33_SRC_EST_REF_CLK_RATIO_B 0x37
+#define DAC33_INTP_CTRL_A 0x38
+#define DAC33_INTP_CTRL_B 0x39
+/* Registers 0x3A - 0x3F Reserved */
+#define DAC33_LDAC_PWR_CTRL 0x40
+#define DAC33_RDAC_PWR_CTRL 0x41
+#define DAC33_OUT_AMP_CM_CTRL 0x42
+#define DAC33_OUT_AMP_PWR_CTRL 0x43
+#define DAC33_OUT_AMP_CTRL 0x44
+#define DAC33_LINEL_TO_LLO_VOL 0x45
+/* Registers 0x45 - 0x47 Reserved */
+#define DAC33_LINER_TO_RLO_VOL 0x48
+#define DAC33_ANA_VOL_SOFT_STEP_CTRL 0x49
+#define DAC33_OSC_TRIM 0x4A
+/* Registers 0x4B - 0x7C Reserved */
+#define DAC33_DEVICE_ID_MSB 0x7D
+#define DAC33_DEVICE_ID_LSB 0x7E
+#define DAC33_DEVICE_REV_ID 0x7F
+
+#define DAC33_CACHEREGNUM 128
+
+/* Bit definitions */
+
+/* DAC33_PWR_CTRL (0x01) */
+#define DAC33_DACRPDNB (0x01 << 0)
+#define DAC33_DACLPDNB (0x01 << 1)
+#define DAC33_OSCPDNB (0x01 << 2)
+#define DAC33_PLLPDNB (0x01 << 3)
+#define DAC33_PDNALLB (0x01 << 4)
+#define DAC33_SOFT_RESET (0x01 << 7)
+
+/* DAC33_INT_OSC_CTRL (0x07) */
+#define DAC33_REFSEL (0x01 << 1)
+
+/* DAC33_INT_OSC_CTRL_B (0x0C) */
+#define DAC33_ADJSTEP(x) (x << 0)
+#define DAC33_ADJTHRSHLD(x) (x << 4)
+
+/* DAC33_INT_OSC_CTRL_C (0x0D) */
+#define DAC33_REFDIV(x) (x << 4)
+
+/* DAC33_INT_OSC_STATUS (0x0E) */
+#define DAC33_OSCSTATUS_IDLE_CALIB (0x00)
+#define DAC33_OSCSTATUS_NORMAL (0x01)
+#define DAC33_OSCSTATUS_ADJUSTMENT (0x03)
+#define DAC33_OSCSTATUS_NOT_USED (0x02)
+
+/* DAC33_SER_AUDIOIF_CTRL_A (0x12) */
+#define DAC33_MSWCLK (0x01 << 0)
+#define DAC33_MSBCLK (0x01 << 1)
+#define DAC33_AFMT_MASK (0x03 << 2)
+#define DAC33_AFMT_I2S (0x00 << 2)
+#define DAC33_AFMT_DSP (0x01 << 2)
+#define DAC33_AFMT_RIGHT_J (0x02 << 2)
+#define DAC33_AFMT_LEFT_J (0x03 << 2)
+#define DAC33_WLEN_MASK (0x03 << 4)
+#define DAC33_WLEN_16 (0x00 << 4)
+#define DAC33_WLEN_20 (0x01 << 4)
+#define DAC33_WLEN_24 (0x02 << 4)
+#define DAC33_WLEN_32 (0x03 << 4)
+#define DAC33_NCYCL_MASK (0x03 << 6)
+#define DAC33_NCYCL_16 (0x00 << 6)
+#define DAC33_NCYCL_20 (0x01 << 6)
+#define DAC33_NCYCL_24 (0x02 << 6)
+#define DAC33_NCYCL_32 (0x03 << 6)
+
+/* DAC33_SER_AUDIOIF_CTRL_B (0x13) */
+#define DAC33_DATA_DELAY_MASK (0x03 << 2)
+#define DAC33_DATA_DELAY(x) (x << 2)
+#define DAC33_BCLKON (0x01 << 5)
+
+/* DAC33_FIFO_CTRL_A (0x15) */
+#define DAC33_WIDTH (0x01 << 0)
+#define DAC33_FBYPAS (0x01 << 1)
+#define DAC33_FAUTO (0x01 << 2)
+#define DAC33_FIFOFLUSH (0x01 << 3)
+
+/*
+ * UTHR, ATHR, LTHR, PREFILL, NSAMPLE (0x16 - 0x1F)
+ * 13-bit values
+*/
+#define DAC33_THRREG(x) (((x) & 0x1FFF) << 3)
+
+/* DAC33_FIFO_IRQ_MASK (0x29) */
+#define DAC33_MNS (0x01 << 0)
+#define DAC33_MPS (0x01 << 1)
+#define DAC33_MAT (0x01 << 2)
+#define DAC33_MLT (0x01 << 3)
+#define DAC33_MUT (0x01 << 4)
+#define DAC33_MUF (0x01 << 5)
+#define DAC33_MOF (0x01 << 6)
+
+#define DAC33_FIFO_IRQ_MODE_MASK (0x03)
+#define DAC33_FIFO_IRQ_MODE_RISING (0x00)
+#define DAC33_FIFO_IRQ_MODE_FALLING (0x01)
+#define DAC33_FIFO_IRQ_MODE_LEVEL (0x02)
+#define DAC33_FIFO_IRQ_MODE_EDGE (0x03)
+
+/* DAC33_FIFO_IRQ_MODE_A (0x2A) */
+#define DAC33_UTM(x) (x << 0)
+#define DAC33_UFM(x) (x << 2)
+#define DAC33_OFM(x) (x << 4)
+
+/* DAC33_FIFO_IRQ_MODE_B (0x2B) */
+#define DAC33_NSM(x) (x << 0)
+#define DAC33_PSM(x) (x << 2)
+#define DAC33_ATM(x) (x << 4)
+#define DAC33_LTM(x) (x << 6)
+
+/* DAC33_DAC_CTRL_A (0x2C) */
+#define DAC33_DACRATE(x) (x << 0)
+#define DAC33_DACDUAL (0x01 << 4)
+#define DAC33_DACLKSEL_MASK (0x03 << 5)
+#define DAC33_DACLKSEL_INTSOC (0x00 << 5)
+#define DAC33_DACLKSEL_PLL (0x01 << 5)
+#define DAC33_DACLKSEL_MCLK (0x02 << 5)
+#define DAC33_DACLKSEL_BCLK (0x03 << 5)
+
+/* DAC33_DAC_CTRL_B (0x2D) */
+#define DAC33_DACSRCR_MASK (0x03 << 0)
+#define DAC33_DACSRCR_MUTE (0x00 << 0)
+#define DAC33_DACSRCR_RIGHT (0x01 << 0)
+#define DAC33_DACSRCR_LEFT (0x02 << 0)
+#define DAC33_DACSRCR_MONOMIX (0x03 << 0)
+#define DAC33_DACSRCL_MASK (0x03 << 2)
+#define DAC33_DACSRCL_MUTE (0x00 << 2)
+#define DAC33_DACSRCL_LEFT (0x01 << 2)
+#define DAC33_DACSRCL_RIGHT (0x02 << 2)
+#define DAC33_DACSRCL_MONOMIX (0x03 << 2)
+#define DAC33_DVOLSTEP_MASK (0x03 << 4)
+#define DAC33_DVOLSTEP_SS_PERFS (0x00 << 4)
+#define DAC33_DVOLSTEP_SS_PER2FS (0x01 << 4)
+#define DAC33_DVOLSTEP_SS_DISABLED (0x02 << 4)
+#define DAC33_DVOLCTRL_MASK (0x03 << 6)
+#define DAC33_DVOLCTRL_LR_INDEPENDENT1 (0x00 << 6)
+#define DAC33_DVOLCTRL_LR_RIGHT_CONTROL (0x01 << 6)
+#define DAC33_DVOLCTRL_LR_LEFT_CONTROL (0x02 << 6)
+#define DAC33_DVOLCTRL_LR_INDEPENDENT2 (0x03 << 6)
+
+/* DAC33_DAC_CTRL_C (0x2E) */
+#define DAC33_DEEMENR (0x01 << 0)
+#define DAC33_EFFENR (0x01 << 1)
+#define DAC33_DEEMENL (0x01 << 2)
+#define DAC33_EFFENL (0x01 << 3)
+#define DAC33_EN3D (0x01 << 4)
+#define DAC33_RESYNMUTE (0x01 << 5)
+#define DAC33_RESYNEN (0x01 << 6)
+
+/* DAC33_ASRC_CTRL_A (0x32) */
+#define DAC33_SRCBYP (0x01 << 0)
+#define DAC33_SRCLKSEL_MASK (0x03 << 1)
+#define DAC33_SRCLKSEL_INTSOC (0x00 << 1)
+#define DAC33_SRCLKSEL_PLL (0x01 << 1)
+#define DAC33_SRCLKSEL_MCLK (0x02 << 1)
+#define DAC33_SRCLKSEL_BCLK (0x03 << 1)
+#define DAC33_SRCLKDIV(x) (x << 3)
+
+/* DAC33_ASRC_CTRL_B (0x33) */
+#define DAC33_SRCSETUP(x) (x << 0)
+#define DAC33_SRCREFSEL (0x01 << 4)
+#define DAC33_SRCREFDIV(x) (x << 5)
+
+/* DAC33_INTP_CTRL_A (0x38) */
+#define DAC33_INTPSEL (0x01 << 0)
+#define DAC33_INTPM_MASK (0x03 << 1)
+#define DAC33_INTPM_ALOW_OPENDRAIN (0x00 << 1)
+#define DAC33_INTPM_ALOW (0x01 << 1)
+#define DAC33_INTPM_AHIGH (0x02 << 1)
+
+/* DAC33_LDAC_PWR_CTRL (0x40) */
+/* DAC33_RDAC_PWR_CTRL (0x41) */
+#define DAC33_DACLRNUM (0x01 << 2)
+#define DAC33_LROUT_GAIN(x) (x << 0)
+
+/* DAC33_ANA_VOL_SOFT_STEP_CTRL (0x49) */
+#define DAC33_VOLCLKSEL (0x01 << 0)
+#define DAC33_VOLCLKEN (0x01 << 1)
+#define DAC33_VOLBYPASS (0x01 << 2)
+
+#define TLV320DAC33_MCLK 0
+#define TLV320DAC33_SLEEPCLK 1
+
+extern struct snd_soc_dai dac33_dai;
+extern struct snd_soc_codec_device soc_codec_dev_tlv320dac33;
+
+#endif /* __TLV320DAC33_H */
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
new file mode 100644
index 00000000000..6b650c1aa3d
--- /dev/null
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -0,0 +1,463 @@
+/*
+ * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * 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/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <sound/tpa6130a2-plat.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "tpa6130a2.h"
+
+static struct i2c_client *tpa6130a2_client;
+
+/* This struct is used to save the context */
+struct tpa6130a2_data {
+ struct mutex mutex;
+ unsigned char regs[TPA6130A2_CACHEREGNUM];
+ int power_gpio;
+ unsigned char power_state;
+};
+
+static int tpa6130a2_i2c_read(int reg)
+{
+ struct tpa6130a2_data *data;
+ int val;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ /* If powered off, return the cached value */
+ if (data->power_state) {
+ val = i2c_smbus_read_byte_data(tpa6130a2_client, reg);
+ if (val < 0)
+ dev_err(&tpa6130a2_client->dev, "Read failed\n");
+ else
+ data->regs[reg] = val;
+ } else {
+ val = data->regs[reg];
+ }
+
+ return val;
+}
+
+static int tpa6130a2_i2c_write(int reg, u8 value)
+{
+ struct tpa6130a2_data *data;
+ int val = 0;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ if (data->power_state) {
+ val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value);
+ if (val < 0)
+ dev_err(&tpa6130a2_client->dev, "Write failed\n");
+ }
+
+ /* Either powered on or off, we save the context */
+ data->regs[reg] = value;
+
+ return val;
+}
+
+static u8 tpa6130a2_read(int reg)
+{
+ struct tpa6130a2_data *data;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ return data->regs[reg];
+}
+
+static void tpa6130a2_initialize(void)
+{
+ struct tpa6130a2_data *data;
+ int i;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ for (i = 1; i < TPA6130A2_REG_VERSION; i++)
+ tpa6130a2_i2c_write(i, data->regs[i]);
+}
+
+static void tpa6130a2_power(int power)
+{
+ struct tpa6130a2_data *data;
+ u8 val;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ mutex_lock(&data->mutex);
+ if (power) {
+ /* Power on */
+ if (data->power_gpio >= 0) {
+ gpio_set_value(data->power_gpio, 1);
+ data->power_state = 1;
+ tpa6130a2_initialize();
+ }
+ /* Clear SWS */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val &= ~TPA6130A2_SWS;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+ } else {
+ /* set SWS */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val |= TPA6130A2_SWS;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+ /* Power off */
+ if (data->power_gpio >= 0) {
+ gpio_set_value(data->power_gpio, 0);
+ data->power_state = 0;
+ }
+ }
+ mutex_unlock(&data->mutex);
+}
+
+static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct tpa6130a2_data *data;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = mc->max;
+ unsigned int invert = mc->invert;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ mutex_lock(&data->mutex);
+
+ ucontrol->value.integer.value[0] =
+ (tpa6130a2_read(reg) >> shift) & mask;
+
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ mask - ucontrol->value.integer.value[0];
+
+ mutex_unlock(&data->mutex);
+ return 0;
+}
+
+static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct tpa6130a2_data *data;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = mc->max;
+ unsigned int invert = mc->invert;
+ unsigned int val = (ucontrol->value.integer.value[0] & mask);
+ unsigned int val_reg;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ if (invert)
+ val = mask - val;
+
+ mutex_lock(&data->mutex);
+
+ val_reg = tpa6130a2_read(reg);
+ if (((val_reg >> shift) & mask) == val) {
+ mutex_unlock(&data->mutex);
+ return 0;
+ }
+
+ val_reg &= ~(mask << shift);
+ val_reg |= val << shift;
+ tpa6130a2_i2c_write(reg, val_reg);
+
+ mutex_unlock(&data->mutex);
+
+ return 1;
+}
+
+/*
+ * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
+ * down in gain.
+ */
+static const unsigned int tpa6130_tlv[] = {
+ TLV_DB_RANGE_HEAD(10),
+ 0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0),
+ 12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0),
+ 14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0),
+ 21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0),
+ 38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0),
+};
+
+static const struct snd_kcontrol_new tpa6130a2_controls[] = {
+ SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
+ TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
+ tpa6130a2_get_reg, tpa6130a2_set_reg,
+ tpa6130_tlv),
+};
+
+/*
+ * Enable or disable channel (left or right)
+ * The bit number for mute and amplifier are the same per channel:
+ * bit 6: Right channel
+ * bit 7: Left channel
+ * in both registers.
+ */
+static void tpa6130a2_channel_enable(u8 channel, int enable)
+{
+ struct tpa6130a2_data *data;
+ u8 val;
+
+ BUG_ON(tpa6130a2_client == NULL);
+ data = i2c_get_clientdata(tpa6130a2_client);
+
+ if (enable) {
+ /* Enable channel */
+ /* Enable amplifier */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val |= channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+
+ /* Unmute channel */
+ val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
+ val &= ~channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
+ } else {
+ /* Disable channel */
+ /* Mute channel */
+ val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
+ val |= channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
+
+ /* Disable amplifier */
+ val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+ val &= ~channel;
+ tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+ }
+}
+
+static int tpa6130a2_left_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 0);
+ break;
+ }
+ return 0;
+}
+
+static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 0);
+ break;
+ }
+ return 0;
+}
+
+static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ tpa6130a2_power(1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tpa6130a2_power(0);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
+ SND_SOC_DAPM_PGA_E("TPA6130A2 Left", SND_SOC_NOPM,
+ 0, 0, NULL, 0, tpa6130a2_left_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("TPA6130A2 Right", SND_SOC_NOPM,
+ 0, 0, NULL, 0, tpa6130a2_right_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("TPA6130A2 Enable", SND_SOC_NOPM,
+ 0, 0, tpa6130a2_supply_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ /* Outputs */
+ SND_SOC_DAPM_HP("TPA6130A2 Headphone Left", NULL),
+ SND_SOC_DAPM_HP("TPA6130A2 Headphone Right", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Left"},
+ {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Right"},
+
+ {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Enable"},
+ {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Enable"},
+};
+
+int tpa6130a2_add_controls(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets,
+ ARRAY_SIZE(tpa6130a2_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ return snd_soc_add_controls(codec, tpa6130a2_controls,
+ ARRAY_SIZE(tpa6130a2_controls));
+
+}
+EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
+
+static int tpa6130a2_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev;
+ struct tpa6130a2_data *data;
+ struct tpa6130a2_platform_data *pdata;
+ int ret;
+
+ dev = &client->dev;
+
+ if (client->dev.platform_data == NULL) {
+ dev_err(dev, "Platform data not set\n");
+ dump_stack();
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL) {
+ dev_err(dev, "Can not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ tpa6130a2_client = client;
+
+ i2c_set_clientdata(tpa6130a2_client, data);
+
+ pdata = client->dev.platform_data;
+ data->power_gpio = pdata->power_gpio;
+
+ mutex_init(&data->mutex);
+
+ /* Set default register values */
+ data->regs[TPA6130A2_REG_CONTROL] = TPA6130A2_SWS;
+ data->regs[TPA6130A2_REG_VOL_MUTE] = TPA6130A2_MUTE_R |
+ TPA6130A2_MUTE_L;
+
+ if (data->power_gpio >= 0) {
+ ret = gpio_request(data->power_gpio, "tpa6130a2 enable");
+ if (ret < 0) {
+ dev_err(dev, "Failed to request power GPIO (%d)\n",
+ data->power_gpio);
+ goto fail;
+ }
+ gpio_direction_output(data->power_gpio, 0);
+ } else {
+ data->power_state = 1;
+ tpa6130a2_initialize();
+ }
+
+ tpa6130a2_power(1);
+
+ /* Read version */
+ ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
+ TPA6130A2_VERSION_MASK;
+ if ((ret != 1) && (ret != 2))
+ dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
+
+ /* Disable the chip */
+ tpa6130a2_power(0);
+
+ return 0;
+fail:
+ kfree(data);
+ i2c_set_clientdata(tpa6130a2_client, NULL);
+ tpa6130a2_client = NULL;
+
+ return ret;
+}
+
+static int tpa6130a2_remove(struct i2c_client *client)
+{
+ struct tpa6130a2_data *data = i2c_get_clientdata(client);
+
+ tpa6130a2_power(0);
+
+ if (data->power_gpio >= 0)
+ gpio_free(data->power_gpio);
+ kfree(data);
+ tpa6130a2_client = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id tpa6130a2_id[] = {
+ { "tpa6130a2", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
+
+static struct i2c_driver tpa6130a2_i2c_driver = {
+ .driver = {
+ .name = "tpa6130a2",
+ .owner = THIS_MODULE,
+ },
+ .probe = tpa6130a2_probe,
+ .remove = __devexit_p(tpa6130a2_remove),
+ .id_table = tpa6130a2_id,
+};
+
+static int __init tpa6130a2_init(void)
+{
+ return i2c_add_driver(&tpa6130a2_i2c_driver);
+}
+
+static void __exit tpa6130a2_exit(void)
+{
+ i2c_del_driver(&tpa6130a2_i2c_driver);
+}
+
+MODULE_AUTHOR("Peter Ujfalusi");
+MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
+MODULE_LICENSE("GPL");
+
+module_init(tpa6130a2_init);
+module_exit(tpa6130a2_exit);
diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h
new file mode 100644
index 00000000000..57e867fd86d
--- /dev/null
+++ b/sound/soc/codecs/tpa6130a2.h
@@ -0,0 +1,61 @@
+/*
+ * ALSA SoC TPA6130A2 amplifier driver
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __TPA6130A2_H__
+#define __TPA6130A2_H__
+
+/* Register addresses */
+#define TPA6130A2_REG_CONTROL 0x01
+#define TPA6130A2_REG_VOL_MUTE 0x02
+#define TPA6130A2_REG_OUT_IMPEDANCE 0x03
+#define TPA6130A2_REG_VERSION 0x04
+
+#define TPA6130A2_CACHEREGNUM (TPA6130A2_REG_VERSION + 1)
+
+/* Register bits */
+/* TPA6130A2_REG_CONTROL (0x01) */
+#define TPA6130A2_SWS (0x01 << 0)
+#define TPA6130A2_TERMAL (0x01 << 1)
+#define TPA6130A2_MODE(x) (x << 4)
+#define TPA6130A2_MODE_STEREO (0x00)
+#define TPA6130A2_MODE_DUAL_MONO (0x01)
+#define TPA6130A2_MODE_BRIDGE (0x02)
+#define TPA6130A2_MODE_MASK (0x03)
+#define TPA6130A2_HP_EN_R (0x01 << 6)
+#define TPA6130A2_HP_EN_L (0x01 << 7)
+
+/* TPA6130A2_REG_VOL_MUTE (0x02) */
+#define TPA6130A2_VOLUME(x) ((x & 0x3f) << 0)
+#define TPA6130A2_MUTE_R (0x01 << 6)
+#define TPA6130A2_MUTE_L (0x01 << 7)
+
+/* TPA6130A2_REG_OUT_IMPEDANCE (0x03) */
+#define TPA6130A2_HIZ_R (0x01 << 0)
+#define TPA6130A2_HIZ_L (0x01 << 1)
+
+/* TPA6130A2_REG_VERSION (0x04) */
+#define TPA6130A2_VERSION_MASK (0x0f)
+
+extern int tpa6130a2_add_controls(struct snd_soc_codec *codec);
+
+#endif /* __TPA6130A2_H__ */
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 4df7c6c61c7..f9121ef7fe5 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -120,9 +120,10 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
/* codec private data */
struct twl4030_priv {
- unsigned int bypass_state;
+ struct snd_soc_codec codec;
+
unsigned int codec_powered;
- unsigned int codec_muted;
+ unsigned int apll_enabled;
struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
@@ -183,19 +184,20 @@ static int twl4030_write(struct snd_soc_codec *codec,
static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = codec->private_data;
- u8 mode;
+ int mode;
if (enable == twl4030->codec_powered)
return;
- mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
if (enable)
- mode |= TWL4030_CODECPDZ;
+ mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
else
- mode &= ~TWL4030_CODECPDZ;
+ mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
- twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
- twl4030->codec_powered = enable;
+ if (mode >= 0) {
+ twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode);
+ twl4030->codec_powered = enable;
+ }
/* REVISIT: this delay is present in TI sample drivers */
/* but there seems to be no TRM requirement for it */
@@ -216,27 +218,25 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
}
-static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
+static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = codec->private_data;
- u8 reg_val;
+ int status;
- if (mute == twl4030->codec_muted)
+ if (enable == twl4030->apll_enabled)
return;
- if (mute) {
- /* Disable PLL */
- reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
- reg_val &= ~TWL4030_APLL_EN;
- twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
- } else {
+ if (enable)
/* Enable PLL */
- reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
- reg_val |= TWL4030_APLL_EN;
- twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
- }
+ status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
+ else
+ /* Disable PLL */
+ status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
- twl4030->codec_muted = mute;
+ if (status >= 0)
+ twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
+
+ twl4030->apll_enabled = enable;
}
static void twl4030_power_up(struct snd_soc_codec *codec)
@@ -613,6 +613,27 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int vibramux_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff);
+ return 0;
+}
+
+static int apll_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ twl4030_apll_enable(w->codec, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ twl4030_apll_enable(w->codec, 0);
+ break;
+ }
+ return 0;
+}
+
static void headset_ramp(struct snd_soc_codec *codec, int ramp)
{
struct snd_soc_device *socdev = codec->socdev;
@@ -724,67 +745,6 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static int bypass_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct soc_mixer_control *m =
- (struct soc_mixer_control *)w->kcontrols->private_value;
- struct twl4030_priv *twl4030 = w->codec->private_data;
- unsigned char reg, misc;
-
- reg = twl4030_read_reg_cache(w->codec, m->reg);
-
- /*
- * bypass_state[0:3] - analog HiFi bypass
- * bypass_state[4] - analog voice bypass
- * bypass_state[5] - digital voice bypass
- * bypass_state[6:7] - digital HiFi bypass
- */
- if (m->reg == TWL4030_REG_VSTPGA) {
- /* Voice digital bypass */
- if (reg)
- twl4030->bypass_state |= (1 << 5);
- else
- twl4030->bypass_state &= ~(1 << 5);
- } else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
- /* Analog bypass */
- if (reg & (1 << m->shift))
- twl4030->bypass_state |=
- (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
- else
- twl4030->bypass_state &=
- ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
- } else if (m->reg == TWL4030_REG_VDL_APGA_CTL) {
- /* Analog voice bypass */
- if (reg & (1 << m->shift))
- twl4030->bypass_state |= (1 << 4);
- else
- twl4030->bypass_state &= ~(1 << 4);
- } else {
- /* Digital bypass */
- if (reg & (0x7 << m->shift))
- twl4030->bypass_state |= (1 << (m->shift ? 7 : 6));
- else
- twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6));
- }
-
- /* Enable master analog loopback mode if any analog switch is enabled*/
- misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1);
- if (twl4030->bypass_state & 0x1F)
- misc |= TWL4030_FMLOOP_EN;
- else
- misc &= ~TWL4030_FMLOOP_EN;
- twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc);
-
- if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) {
- if (twl4030->bypass_state)
- twl4030_codec_mute(w->codec, 0);
- else
- twl4030_codec_mute(w->codec, 1);
- }
- return 0;
-}
-
/*
* Some of the gain controls in TWL (mostly those which are associated with
* the outputs) are implemented in an interesting way:
@@ -1192,32 +1152,28 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_NOPM, 0, 0),
/* Analog bypasses */
- SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_abypassr1_control, bypass_event,
- SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_abypassl1_control,
- bypass_event, SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_abypassr2_control,
- bypass_event, SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_abypassl2_control,
- bypass_event, SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_abypassv_control,
- bypass_event, SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassr1_control),
+ SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassl1_control),
+ SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassr2_control),
+ SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassl2_control),
+ SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_abypassv_control),
+
+ /* Master analog loopback switch */
+ SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0,
+ NULL, 0),
/* Digital bypasses */
- SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_dbypassl_control, bypass_event,
- SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_dbypassr_control, bypass_event,
- SND_SOC_DAPM_POST_REG),
- SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
- &twl4030_dapm_dbypassv_control, bypass_event,
- SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassl_control),
+ SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassr_control),
+ SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_dbypassv_control),
/* Digital mixers, power control for the physical DACs */
SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer",
@@ -1243,6 +1199,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer",
TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
+ SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
+
/* Output MIXER controls */
/* Earpiece */
SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
@@ -1308,8 +1267,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
0, 0, NULL, 0, handsfreerpga_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* Vibra */
- SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
- &twl4030_dapm_vibra_control),
+ SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
+ &twl4030_dapm_vibra_control, vibramux_event,
+ SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_vibrapath_control),
@@ -1369,6 +1329,13 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Digital R2 Playback Mixer", NULL, "DAC Right2"},
{"Digital Voice Playback Mixer", NULL, "DAC Voice"},
+ /* Supply for the digital part (APLL) */
+ {"Digital R1 Playback Mixer", NULL, "APLL Enable"},
+ {"Digital L1 Playback Mixer", NULL, "APLL Enable"},
+ {"Digital R2 Playback Mixer", NULL, "APLL Enable"},
+ {"Digital L2 Playback Mixer", NULL, "APLL Enable"},
+ {"Digital Voice Playback Mixer", NULL, "APLL Enable"},
+
{"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"},
{"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
{"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
@@ -1482,6 +1449,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ADC Virtual Left2", NULL, "TX2 Capture Route"},
{"ADC Virtual Right2", NULL, "TX2 Capture Route"},
+ {"ADC Virtual Left1", NULL, "APLL Enable"},
+ {"ADC Virtual Right1", NULL, "APLL Enable"},
+ {"ADC Virtual Left2", NULL, "APLL Enable"},
+ {"ADC Virtual Right2", NULL, "APLL Enable"},
+
/* Analog bypass routes */
{"Right1 Analog Loopback", "Switch", "Analog Right"},
{"Left1 Analog Loopback", "Switch", "Analog Left"},
@@ -1489,6 +1461,13 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Left2 Analog Loopback", "Switch", "Analog Left"},
{"Voice Analog Loopback", "Switch", "Analog Left"},
+ /* Supply for the Analog loopbacks */
+ {"Right1 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Left1 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Right2 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Left2 Analog Loopback", NULL, "FM Loop Enable"},
+ {"Voice Analog Loopback", NULL, "FM Loop Enable"},
+
{"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
{"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
{"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
@@ -1520,25 +1499,14 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec)
static int twl4030_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct twl4030_priv *twl4030 = codec->private_data;
-
switch (level) {
case SND_SOC_BIAS_ON:
- twl4030_codec_mute(codec, 0);
break;
case SND_SOC_BIAS_PREPARE:
- twl4030_power_up(codec);
- if (twl4030->bypass_state)
- twl4030_codec_mute(codec, 0);
- else
- twl4030_codec_mute(codec, 1);
break;
case SND_SOC_BIAS_STANDBY:
- twl4030_power_up(codec);
- if (twl4030->bypass_state)
- twl4030_codec_mute(codec, 0);
- else
- twl4030_codec_mute(codec, 1);
+ if (codec->bias_level == SND_SOC_BIAS_OFF)
+ twl4030_power_up(codec);
break;
case SND_SOC_BIAS_OFF:
twl4030_power_down(codec);
@@ -1785,19 +1753,21 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
{
struct snd_soc_codec *codec = codec_dai->codec;
struct twl4030_priv *twl4030 = codec->private_data;
- u8 infreq;
+ u8 apll_ctrl;
+ apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
+ apll_ctrl &= ~TWL4030_APLL_INFREQ;
switch (freq) {
case 19200000:
- infreq = TWL4030_APLL_INFREQ_19200KHZ;
+ apll_ctrl |= TWL4030_APLL_INFREQ_19200KHZ;
twl4030->sysclk = 19200;
break;
case 26000000:
- infreq = TWL4030_APLL_INFREQ_26000KHZ;
+ apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ;
twl4030->sysclk = 26000;
break;
case 38400000:
- infreq = TWL4030_APLL_INFREQ_38400KHZ;
+ apll_ctrl |= TWL4030_APLL_INFREQ_38400KHZ;
twl4030->sysclk = 38400;
break;
default:
@@ -1806,8 +1776,7 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- infreq |= TWL4030_APLL_EN;
- twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+ twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl);
return 0;
}
@@ -1989,11 +1958,13 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
- u8 infreq;
+ u8 apll_ctrl;
+ apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
+ apll_ctrl &= ~TWL4030_APLL_INFREQ;
switch (freq) {
case 26000000:
- infreq = TWL4030_APLL_INFREQ_26000KHZ;
+ apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ;
break;
default:
printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n",
@@ -2001,8 +1972,7 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- infreq |= TWL4030_APLL_EN;
- twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+ twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl);
return 0;
}
@@ -2121,7 +2091,7 @@ struct snd_soc_dai twl4030_dai[] = {
};
EXPORT_SYMBOL_GPL(twl4030_dai);
-static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
+static int twl4030_soc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
@@ -2131,7 +2101,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int twl4030_resume(struct platform_device *pdev)
+static int twl4030_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
@@ -2141,32 +2111,21 @@ static int twl4030_resume(struct platform_device *pdev)
return 0;
}
-/*
- * initialize the driver
- * register the mixer and dsp interfaces with the kernel
- */
+static struct snd_soc_codec *twl4030_codec;
-static int twl4030_init(struct snd_soc_device *socdev)
+static int twl4030_soc_probe(struct platform_device *pdev)
{
- struct snd_soc_codec *codec = socdev->card->codec;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct twl4030_setup_data *setup = socdev->codec_data;
- struct twl4030_priv *twl4030 = codec->private_data;
- int ret = 0;
+ struct snd_soc_codec *codec;
+ struct twl4030_priv *twl4030;
+ int ret;
- printk(KERN_INFO "TWL4030 Audio Codec init \n");
+ BUG_ON(!twl4030_codec);
- codec->name = "twl4030";
- codec->owner = THIS_MODULE;
- codec->read = twl4030_read_reg_cache;
- codec->write = twl4030_write;
- codec->set_bias_level = twl4030_set_bias_level;
- codec->dai = twl4030_dai;
- codec->num_dai = ARRAY_SIZE(twl4030_dai),
- codec->reg_cache_size = sizeof(twl4030_reg);
- codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
- GFP_KERNEL);
- if (codec->reg_cache == NULL)
- return -ENOMEM;
+ codec = twl4030_codec;
+ twl4030 = codec->private_data;
+ socdev->card->codec = codec;
/* Configuration for headset ramp delay from setup data */
if (setup) {
@@ -2188,100 +2147,159 @@ static int twl4030_init(struct snd_soc_device *socdev)
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
- printk(KERN_ERR "twl4030: failed to create pcms\n");
- goto pcm_err;
+ dev_err(&pdev->dev, "failed to create pcms\n");
+ return ret;
}
- twl4030_init_chip(codec);
-
- /* power on device */
- twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
snd_soc_add_controls(codec, twl4030_snd_controls,
ARRAY_SIZE(twl4030_snd_controls));
twl4030_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
- printk(KERN_ERR "twl4030: failed to register card\n");
+ dev_err(&pdev->dev, "failed to register card\n");
goto card_err;
}
- return ret;
+ return 0;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-pcm_err:
- kfree(codec->reg_cache);
+
return ret;
}
-static struct snd_soc_device *twl4030_socdev;
-
-static int twl4030_probe(struct platform_device *pdev)
+static int twl4030_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+static int __devinit twl4030_codec_probe(struct platform_device *pdev)
+{
+ struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data;
struct snd_soc_codec *codec;
struct twl4030_priv *twl4030;
+ int ret;
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
- return -ENOMEM;
+ if (!pdata || !(pdata->audio_mclk == 19200000 ||
+ pdata->audio_mclk == 26000000 ||
+ pdata->audio_mclk == 38400000)) {
+ dev_err(&pdev->dev, "Invalid platform_data\n");
+ return -EINVAL;
+ }
twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
if (twl4030 == NULL) {
- kfree(codec);
+ dev_err(&pdev->dev, "Can not allocate memroy\n");
return -ENOMEM;
}
+ codec = &twl4030->codec;
codec->private_data = twl4030;
- socdev->card->codec = codec;
+ codec->dev = &pdev->dev;
+ twl4030_dai[0].dev = &pdev->dev;
+ twl4030_dai[1].dev = &pdev->dev;
+
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
- twl4030_socdev = socdev;
- twl4030_init(socdev);
+ codec->name = "twl4030";
+ codec->owner = THIS_MODULE;
+ codec->read = twl4030_read_reg_cache;
+ codec->write = twl4030_write;
+ codec->set_bias_level = twl4030_set_bias_level;
+ codec->dai = twl4030_dai;
+ codec->num_dai = ARRAY_SIZE(twl4030_dai),
+ codec->reg_cache_size = sizeof(twl4030_reg);
+ codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL) {
+ ret = -ENOMEM;
+ goto error_cache;
+ }
+
+ platform_set_drvdata(pdev, twl4030);
+ twl4030_codec = codec;
+
+ /* Set the defaults, and power up the codec */
+ twl4030_init_chip(codec);
+ twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto error_codec;
+ }
+
+ ret = snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ goto error_codec;
+ }
return 0;
+
+error_codec:
+ twl4030_power_down(codec);
+ kfree(codec->reg_cache);
+error_cache:
+ kfree(twl4030);
+ return ret;
}
-static int twl4030_remove(struct platform_device *pdev)
+static int __devexit twl4030_codec_remove(struct platform_device *pdev)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl4030_priv *twl4030 = platform_get_drvdata(pdev);
- printk(KERN_INFO "TWL4030 Audio Codec remove\n");
- twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
- kfree(codec->private_data);
- kfree(codec);
+ kfree(twl4030);
+ twl4030_codec = NULL;
return 0;
}
-struct snd_soc_codec_device soc_codec_dev_twl4030 = {
- .probe = twl4030_probe,
- .remove = twl4030_remove,
- .suspend = twl4030_suspend,
- .resume = twl4030_resume,
+MODULE_ALIAS("platform:twl4030_codec_audio");
+
+static struct platform_driver twl4030_codec_driver = {
+ .probe = twl4030_codec_probe,
+ .remove = __devexit_p(twl4030_codec_remove),
+ .driver = {
+ .name = "twl4030_codec_audio",
+ .owner = THIS_MODULE,
+ },
};
-EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
static int __init twl4030_modinit(void)
{
- return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+ return platform_driver_register(&twl4030_codec_driver);
}
module_init(twl4030_modinit);
static void __exit twl4030_exit(void)
{
- snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+ platform_driver_unregister(&twl4030_codec_driver);
}
module_exit(twl4030_exit);
+struct snd_soc_codec_device soc_codec_dev_twl4030 = {
+ .probe = twl4030_soc_probe,
+ .remove = twl4030_soc_remove,
+ .suspend = twl4030_soc_suspend,
+ .resume = twl4030_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
+
MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
MODULE_AUTHOR("Steve Sakoman");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index 2b4bfa23f98..dd6396ec9c7 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -22,245 +22,13 @@
#ifndef __TWL4030_AUDIO_H__
#define __TWL4030_AUDIO_H__
-#define TWL4030_REG_CODEC_MODE 0x1
-#define TWL4030_REG_OPTION 0x2
-#define TWL4030_REG_UNKNOWN 0x3
-#define TWL4030_REG_MICBIAS_CTL 0x4
-#define TWL4030_REG_ANAMICL 0x5
-#define TWL4030_REG_ANAMICR 0x6
-#define TWL4030_REG_AVADC_CTL 0x7
-#define TWL4030_REG_ADCMICSEL 0x8
-#define TWL4030_REG_DIGMIXING 0x9
-#define TWL4030_REG_ATXL1PGA 0xA
-#define TWL4030_REG_ATXR1PGA 0xB
-#define TWL4030_REG_AVTXL2PGA 0xC
-#define TWL4030_REG_AVTXR2PGA 0xD
-#define TWL4030_REG_AUDIO_IF 0xE
-#define TWL4030_REG_VOICE_IF 0xF
-#define TWL4030_REG_ARXR1PGA 0x10
-#define TWL4030_REG_ARXL1PGA 0x11
-#define TWL4030_REG_ARXR2PGA 0x12
-#define TWL4030_REG_ARXL2PGA 0x13
-#define TWL4030_REG_VRXPGA 0x14
-#define TWL4030_REG_VSTPGA 0x15
-#define TWL4030_REG_VRX2ARXPGA 0x16
-#define TWL4030_REG_AVDAC_CTL 0x17
-#define TWL4030_REG_ARX2VTXPGA 0x18
-#define TWL4030_REG_ARXL1_APGA_CTL 0x19
-#define TWL4030_REG_ARXR1_APGA_CTL 0x1A
-#define TWL4030_REG_ARXL2_APGA_CTL 0x1B
-#define TWL4030_REG_ARXR2_APGA_CTL 0x1C
-#define TWL4030_REG_ATX2ARXPGA 0x1D
-#define TWL4030_REG_BT_IF 0x1E
-#define TWL4030_REG_BTPGA 0x1F
-#define TWL4030_REG_BTSTPGA 0x20
-#define TWL4030_REG_EAR_CTL 0x21
-#define TWL4030_REG_HS_SEL 0x22
-#define TWL4030_REG_HS_GAIN_SET 0x23
-#define TWL4030_REG_HS_POPN_SET 0x24
-#define TWL4030_REG_PREDL_CTL 0x25
-#define TWL4030_REG_PREDR_CTL 0x26
-#define TWL4030_REG_PRECKL_CTL 0x27
-#define TWL4030_REG_PRECKR_CTL 0x28
-#define TWL4030_REG_HFL_CTL 0x29
-#define TWL4030_REG_HFR_CTL 0x2A
-#define TWL4030_REG_ALC_CTL 0x2B
-#define TWL4030_REG_ALC_SET1 0x2C
-#define TWL4030_REG_ALC_SET2 0x2D
-#define TWL4030_REG_BOOST_CTL 0x2E
-#define TWL4030_REG_SOFTVOL_CTL 0x2F
-#define TWL4030_REG_DTMF_FREQSEL 0x30
-#define TWL4030_REG_DTMF_TONEXT1H 0x31
-#define TWL4030_REG_DTMF_TONEXT1L 0x32
-#define TWL4030_REG_DTMF_TONEXT2H 0x33
-#define TWL4030_REG_DTMF_TONEXT2L 0x34
-#define TWL4030_REG_DTMF_TONOFF 0x35
-#define TWL4030_REG_DTMF_WANONOFF 0x36
-#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37
-#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38
-#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39
-#define TWL4030_REG_APLL_CTL 0x3A
-#define TWL4030_REG_DTMF_CTL 0x3B
-#define TWL4030_REG_DTMF_PGA_CTL2 0x3C
-#define TWL4030_REG_DTMF_PGA_CTL1 0x3D
-#define TWL4030_REG_MISC_SET_1 0x3E
-#define TWL4030_REG_PCMBTMUX 0x3F
-#define TWL4030_REG_RX_PATH_SEL 0x43
-#define TWL4030_REG_VDL_APGA_CTL 0x44
-#define TWL4030_REG_VIBRA_CTL 0x45
-#define TWL4030_REG_VIBRA_SET 0x46
-#define TWL4030_REG_VIBRA_PWM_SET 0x47
-#define TWL4030_REG_ANAMIC_GAIN 0x48
-#define TWL4030_REG_MISC_SET_2 0x49
-#define TWL4030_REG_SW_SHADOW 0x4A
+/* Register descriptions are here */
+#include <linux/mfd/twl4030-codec.h>
+/* Sgadow register used by the audio driver */
+#define TWL4030_REG_SW_SHADOW 0x4A
#define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1)
-/* Bitfield Definitions */
-
-/* TWL4030_CODEC_MODE (0x01) Fields */
-
-#define TWL4030_APLL_RATE 0xF0
-#define TWL4030_APLL_RATE_8000 0x00
-#define TWL4030_APLL_RATE_11025 0x10
-#define TWL4030_APLL_RATE_12000 0x20
-#define TWL4030_APLL_RATE_16000 0x40
-#define TWL4030_APLL_RATE_22050 0x50
-#define TWL4030_APLL_RATE_24000 0x60
-#define TWL4030_APLL_RATE_32000 0x80
-#define TWL4030_APLL_RATE_44100 0x90
-#define TWL4030_APLL_RATE_48000 0xA0
-#define TWL4030_APLL_RATE_96000 0xE0
-#define TWL4030_SEL_16K 0x08
-#define TWL4030_CODECPDZ 0x02
-#define TWL4030_OPT_MODE 0x01
-#define TWL4030_OPTION_1 (1 << 0)
-#define TWL4030_OPTION_2 (0 << 0)
-
-/* TWL4030_OPTION (0x02) Fields */
-
-#define TWL4030_ATXL1_EN (1 << 0)
-#define TWL4030_ATXR1_EN (1 << 1)
-#define TWL4030_ATXL2_VTXL_EN (1 << 2)
-#define TWL4030_ATXR2_VTXR_EN (1 << 3)
-#define TWL4030_ARXL1_VRX_EN (1 << 4)
-#define TWL4030_ARXR1_EN (1 << 5)
-#define TWL4030_ARXL2_EN (1 << 6)
-#define TWL4030_ARXR2_EN (1 << 7)
-
-/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
-
-#define TWL4030_MICBIAS2_CTL 0x40
-#define TWL4030_MICBIAS1_CTL 0x20
-#define TWL4030_HSMICBIAS_EN 0x04
-#define TWL4030_MICBIAS2_EN 0x02
-#define TWL4030_MICBIAS1_EN 0x01
-
-/* ANAMICL (0x05) Fields */
-
-#define TWL4030_CNCL_OFFSET_START 0x80
-#define TWL4030_OFFSET_CNCL_SEL 0x60
-#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00
-#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20
-#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40
-#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60
-#define TWL4030_MICAMPL_EN 0x10
-#define TWL4030_CKMIC_EN 0x08
-#define TWL4030_AUXL_EN 0x04
-#define TWL4030_HSMIC_EN 0x02
-#define TWL4030_MAINMIC_EN 0x01
-
-/* ANAMICR (0x06) Fields */
-
-#define TWL4030_MICAMPR_EN 0x10
-#define TWL4030_AUXR_EN 0x04
-#define TWL4030_SUBMIC_EN 0x01
-
-/* AVADC_CTL (0x07) Fields */
-
-#define TWL4030_ADCL_EN 0x08
-#define TWL4030_AVADC_CLK_PRIORITY 0x04
-#define TWL4030_ADCR_EN 0x02
-
-/* TWL4030_REG_ADCMICSEL (0x08) Fields */
-
-#define TWL4030_DIGMIC1_EN 0x08
-#define TWL4030_TX2IN_SEL 0x04
-#define TWL4030_DIGMIC0_EN 0x02
-#define TWL4030_TX1IN_SEL 0x01
-
-/* AUDIO_IF (0x0E) Fields */
-
-#define TWL4030_AIF_SLAVE_EN 0x80
-#define TWL4030_DATA_WIDTH 0x60
-#define TWL4030_DATA_WIDTH_16S_16W 0x00
-#define TWL4030_DATA_WIDTH_32S_16W 0x40
-#define TWL4030_DATA_WIDTH_32S_24W 0x60
-#define TWL4030_AIF_FORMAT 0x18
-#define TWL4030_AIF_FORMAT_CODEC 0x00
-#define TWL4030_AIF_FORMAT_LEFT 0x08
-#define TWL4030_AIF_FORMAT_RIGHT 0x10
-#define TWL4030_AIF_FORMAT_TDM 0x18
-#define TWL4030_AIF_TRI_EN 0x04
-#define TWL4030_CLK256FS_EN 0x02
-#define TWL4030_AIF_EN 0x01
-
-/* VOICE_IF (0x0F) Fields */
-
-#define TWL4030_VIF_SLAVE_EN 0x80
-#define TWL4030_VIF_DIN_EN 0x40
-#define TWL4030_VIF_DOUT_EN 0x20
-#define TWL4030_VIF_SWAP 0x10
-#define TWL4030_VIF_FORMAT 0x08
-#define TWL4030_VIF_TRI_EN 0x04
-#define TWL4030_VIF_SUB_EN 0x02
-#define TWL4030_VIF_EN 0x01
-
-/* EAR_CTL (0x21) */
-#define TWL4030_EAR_GAIN 0x30
-
-/* HS_GAIN_SET (0x23) Fields */
-
-#define TWL4030_HSR_GAIN 0x0C
-#define TWL4030_HSR_GAIN_PWR_DOWN 0x00
-#define TWL4030_HSR_GAIN_PLUS_6DB 0x04
-#define TWL4030_HSR_GAIN_0DB 0x08
-#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C
-#define TWL4030_HSL_GAIN 0x03
-#define TWL4030_HSL_GAIN_PWR_DOWN 0x00
-#define TWL4030_HSL_GAIN_PLUS_6DB 0x01
-#define TWL4030_HSL_GAIN_0DB 0x02
-#define TWL4030_HSL_GAIN_MINUS_6DB 0x03
-
-/* HS_POPN_SET (0x24) Fields */
-
-#define TWL4030_VMID_EN 0x40
-#define TWL4030_EXTMUTE 0x20
-#define TWL4030_RAMP_DELAY 0x1C
-#define TWL4030_RAMP_DELAY_20MS 0x00
-#define TWL4030_RAMP_DELAY_40MS 0x04
-#define TWL4030_RAMP_DELAY_81MS 0x08
-#define TWL4030_RAMP_DELAY_161MS 0x0C
-#define TWL4030_RAMP_DELAY_323MS 0x10
-#define TWL4030_RAMP_DELAY_645MS 0x14
-#define TWL4030_RAMP_DELAY_1291MS 0x18
-#define TWL4030_RAMP_DELAY_2581MS 0x1C
-#define TWL4030_RAMP_EN 0x02
-
-/* PREDL_CTL (0x25) */
-#define TWL4030_PREDL_GAIN 0x30
-
-/* PREDR_CTL (0x26) */
-#define TWL4030_PREDR_GAIN 0x30
-
-/* PRECKL_CTL (0x27) */
-#define TWL4030_PRECKL_GAIN 0x30
-
-/* PRECKR_CTL (0x28) */
-#define TWL4030_PRECKR_GAIN 0x30
-
-/* HFL_CTL (0x29, 0x2A) Fields */
-#define TWL4030_HF_CTL_HB_EN 0x04
-#define TWL4030_HF_CTL_LOOP_EN 0x08
-#define TWL4030_HF_CTL_RAMP_EN 0x10
-#define TWL4030_HF_CTL_REF_EN 0x20
-
-/* APLL_CTL (0x3A) Fields */
-
-#define TWL4030_APLL_EN 0x10
-#define TWL4030_APLL_INFREQ 0x0F
-#define TWL4030_APLL_INFREQ_19200KHZ 0x05
-#define TWL4030_APLL_INFREQ_26000KHZ 0x06
-#define TWL4030_APLL_INFREQ_38400KHZ 0x0F
-
-/* REG_MISC_SET_1 (0x3E) Fields */
-
-#define TWL4030_CLK64_EN 0x80
-#define TWL4030_SCRAMBLE_EN 0x40
-#define TWL4030_FMLOOP_EN 0x20
-#define TWL4030_SMOOTH_ANAVOL_EN 0x02
-#define TWL4030_DIGMIC_LR_SWAP_EN 0x01
-
/* TWL4030_REG_SW_SHADOW (0x4A) Fields */
#define TWL4030_HFL_EN 0x01
#define TWL4030_HFR_EN 0x02
@@ -279,3 +47,5 @@ struct twl4030_setup_data {
};
#endif /* End of __TWL4030_AUDIO_H__ */
+
+
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 593d5b9c9f0..714114b50d1 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1101,7 +1101,7 @@ static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
}
static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in,
+ int pll_id, int source, unsigned int freq_in,
unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -1680,21 +1680,6 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m)
-{
- return snd_soc_suspend_device(&pdev->dev);
-}
-
-static int wm8350_codec_resume(struct platform_device *pdev)
-{
- return snd_soc_resume_device(&pdev->dev);
-}
-#else
-#define wm8350_codec_suspend NULL
-#define wm8350_codec_resume NULL
-#endif
-
static struct platform_driver wm8350_codec_driver = {
.driver = {
.name = "wm8350-codec",
@@ -1702,8 +1687,6 @@ static struct platform_driver wm8350_codec_driver = {
},
.probe = wm8350_codec_probe,
.remove = __devexit_p(wm8350_codec_remove),
- .suspend = wm8350_codec_suspend,
- .resume = wm8350_codec_resume,
};
static __init int wm8350_init(void)
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index b9ef4d91522..bd7eecba20f 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -1011,7 +1011,8 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors,
}
static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
- unsigned int freq_in, unsigned int freq_out)
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct wm8400_priv *wm8400 = codec->private_data;
@@ -1558,21 +1559,6 @@ static int __exit wm8400_codec_remove(struct platform_device *dev)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg)
-{
- return snd_soc_suspend_device(&pdev->dev);
-}
-
-static int wm8400_pdev_resume(struct platform_device *pdev)
-{
- return snd_soc_resume_device(&pdev->dev);
-}
-#else
-#define wm8400_pdev_suspend NULL
-#define wm8400_pdev_resume NULL
-#endif
-
static struct platform_driver wm8400_codec_driver = {
.driver = {
.name = "wm8400-codec",
@@ -1580,8 +1566,6 @@ static struct platform_driver wm8400_codec_driver = {
},
.probe = wm8400_codec_probe,
.remove = __exit_p(wm8400_codec_remove),
- .suspend = wm8400_pdev_suspend,
- .resume = wm8400_pdev_resume,
};
static int __init wm8400_codec_init(void)
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 060d5d06ba9..5702435af81 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -271,8 +271,8 @@ static void pll_factors(unsigned int target, unsigned int source)
pll_div.k = K;
}
-static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 25870a4652f..268cab21c2c 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -638,21 +638,6 @@ static __devexit int wm8523_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8523_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
- return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8523_i2c_resume(struct i2c_client *i2c)
-{
- return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8523_i2c_suspend NULL
-#define wm8523_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8523_i2c_id[] = {
{ "wm8523", 0 },
{ }
@@ -666,8 +651,6 @@ static struct i2c_driver wm8523_i2c_driver = {
},
.probe = wm8523_i2c_probe,
.remove = __devexit_p(wm8523_i2c_remove),
- .suspend = wm8523_i2c_suspend,
- .resume = wm8523_i2c_resume,
.id_table = wm8523_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 6bded8c7815..a09b23e0366 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -407,8 +407,8 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target,
return 0;
}
-static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
int offset;
struct snd_soc_codec *codec = codec_dai->codec;
@@ -961,21 +961,6 @@ static int wm8580_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8580_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8580_i2c_suspend NULL
-#define wm8580_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8580_i2c_id[] = {
{ "wm8580", 0 },
{ }
@@ -989,8 +974,6 @@ static struct i2c_driver wm8580_i2c_driver = {
},
.probe = wm8580_i2c_probe,
.remove = wm8580_i2c_remove,
- .suspend = wm8580_i2c_suspend,
- .resume = wm8580_i2c_resume,
.id_table = wm8580_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
new file mode 100644
index 00000000000..54189fbf9e9
--- /dev/null
+++ b/sound/soc/codecs/wm8711.c
@@ -0,0 +1,642 @@
+/*
+ * wm8711.c -- WM8711 ALSA SoC Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.c by Richard Purdie
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.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/tlv.h>
+#include <sound/initval.h>
+
+#include "wm8711.h"
+
+static struct snd_soc_codec *wm8711_codec;
+
+/* codec private data */
+struct wm8711_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[WM8711_CACHEREGNUM];
+ unsigned int sysclk;
+};
+
+/*
+ * wm8711 register cache
+ * We can't read the WM8711 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const u16 wm8711_reg[WM8711_CACHEREGNUM] = {
+ 0x0079, 0x0079, 0x000a, 0x0008,
+ 0x009f, 0x000a, 0x0000, 0x0000
+};
+
+#define wm8711_reset(c) snd_soc_write(c, WM8711_RESET, 0)
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
+static const struct snd_kcontrol_new wm8711_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
+ 7, 1, 0),
+
+};
+
+/* Output Mixer */
+static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
+ &wm8711_output_mixer_controls[0],
+ ARRAY_SIZE(wm8711_output_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* output mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
+
+ /* outputs */
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+};
+
+static int wm8711_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8711_dapm_widgets,
+ ARRAY_SIZE(wm8711_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+struct _coeff_div {
+ u32 mclk;
+ u32 rate;
+ u16 fs;
+ u8 sr:4;
+ u8 bosr:1;
+ u8 usb:1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ /* 48k */
+ {12288000, 48000, 256, 0x0, 0x0, 0x0},
+ {18432000, 48000, 384, 0x0, 0x1, 0x0},
+ {12000000, 48000, 250, 0x0, 0x0, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0x6, 0x0, 0x0},
+ {18432000, 32000, 576, 0x6, 0x1, 0x0},
+ {12000000, 32000, 375, 0x6, 0x0, 0x1},
+
+ /* 8k */
+ {12288000, 8000, 1536, 0x3, 0x0, 0x0},
+ {18432000, 8000, 2304, 0x3, 0x1, 0x0},
+ {11289600, 8000, 1408, 0xb, 0x0, 0x0},
+ {16934400, 8000, 2112, 0xb, 0x1, 0x0},
+ {12000000, 8000, 1500, 0x3, 0x0, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0x7, 0x0, 0x0},
+ {18432000, 96000, 192, 0x7, 0x1, 0x0},
+ {12000000, 96000, 125, 0x7, 0x0, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x8, 0x0, 0x0},
+ {16934400, 44100, 384, 0x8, 0x1, 0x0},
+ {12000000, 44100, 272, 0x8, 0x1, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0xf, 0x0, 0x0},
+ {16934400, 88200, 192, 0xf, 0x1, 0x0},
+ {12000000, 88200, 136, 0xf, 0x1, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return 0;
+}
+
+static int wm8711_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 wm8711_priv *wm8711 = codec->private_data;
+ u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0xfffc;
+ int i = get_coeff(wm8711->sysclk, params_rate(params));
+ u16 srate = (coeff_div[i].sr << 2) |
+ (coeff_div[i].bosr << 1) | coeff_div[i].usb;
+
+ snd_soc_write(codec, WM8711_SRATE, srate);
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x0004;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x0008;
+ break;
+ }
+
+ snd_soc_write(codec, WM8711_IFACE, iface);
+ return 0;
+}
+
+static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ /* set active */
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0001);
+
+ return 0;
+}
+
+static void wm8711_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ /* deactivate */
+ if (!codec->active) {
+ udelay(50);
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+ }
+}
+
+static int wm8711_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8711_APDIGI) & 0xfff7;
+
+ if (mute)
+ snd_soc_write(codec, WM8711_APDIGI, mute_reg | 0x8);
+ else
+ snd_soc_write(codec, WM8711_APDIGI, mute_reg);
+
+ return 0;
+}
+
+static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8711_priv *wm8711 = codec->private_data;
+
+ switch (freq) {
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ wm8711->sysclk = freq;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface |= 0x0040;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0013;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0090;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0080;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0010;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set iface */
+ snd_soc_write(codec, WM8711_IFACE, iface);
+ return 0;
+}
+
+
+static int wm8711_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg = snd_soc_read(codec, WM8711_PWR) & 0xff7f;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_write(codec, WM8711_PWR, reg);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+ snd_soc_write(codec, WM8711_PWR, 0xffff);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8711_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8711_ops = {
+ .prepare = wm8711_pcm_prepare,
+ .hw_params = wm8711_hw_params,
+ .shutdown = wm8711_shutdown,
+ .digital_mute = wm8711_mute,
+ .set_sysclk = wm8711_set_dai_sysclk,
+ .set_fmt = wm8711_set_dai_fmt,
+};
+
+struct snd_soc_dai wm8711_dai = {
+ .name = "WM8711",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8711_RATES,
+ .formats = WM8711_FORMATS,
+ },
+ .ops = &wm8711_ops,
+};
+EXPORT_SYMBOL_GPL(wm8711_dai);
+
+static int wm8711_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+ wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8711_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8711_reg); i++) {
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+ wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ wm8711_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+static int wm8711_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (wm8711_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = wm8711_codec;
+ codec = wm8711_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, wm8711_snd_controls,
+ ARRAY_SIZE(wm8711_snd_controls));
+ wm8711_add_widgets(codec);
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int wm8711_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8711 = {
+ .probe = wm8711_probe,
+ .remove = wm8711_remove,
+ .suspend = wm8711_suspend,
+ .resume = wm8711_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711);
+
+static int wm8711_register(struct wm8711_priv *wm8711,
+ enum snd_soc_control_type control)
+{
+ int ret;
+ struct snd_soc_codec *codec = &wm8711->codec;
+ u16 reg;
+
+ if (wm8711_codec) {
+ dev_err(codec->dev, "Another WM8711 is registered\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = wm8711;
+ codec->name = "WM8711";
+ codec->owner = THIS_MODULE;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8711_set_bias_level;
+ codec->dai = &wm8711_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = WM8711_CACHEREGNUM;
+ codec->reg_cache = &wm8711->reg_cache;
+
+ memcpy(codec->reg_cache, wm8711_reg, sizeof(wm8711_reg));
+
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ ret = wm8711_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err;
+ }
+
+ wm8711_dai.dev = codec->dev;
+
+ wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Latch the update bits */
+ reg = snd_soc_read(codec, WM8711_LOUT1V);
+ snd_soc_write(codec, WM8711_LOUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8711_ROUT1V);
+ snd_soc_write(codec, WM8711_ROUT1V, reg | 0x0100);
+
+ wm8711_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dai(&wm8711_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(wm8711);
+ return ret;
+}
+
+static void wm8711_unregister(struct wm8711_priv *wm8711)
+{
+ wm8711_set_bias_level(&wm8711->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dai(&wm8711_dai);
+ snd_soc_unregister_codec(&wm8711->codec);
+ kfree(wm8711);
+ wm8711_codec = NULL;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8711_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_codec *codec;
+ struct wm8711_priv *wm8711;
+
+ wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+ if (wm8711 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8711->codec;
+ codec->control_data = spi;
+ codec->dev = &spi->dev;
+
+ dev_set_drvdata(&spi->dev, wm8711);
+
+ return wm8711_register(wm8711, SND_SOC_SPI);
+}
+
+static int __devexit wm8711_spi_remove(struct spi_device *spi)
+{
+ struct wm8711_priv *wm8711 = dev_get_drvdata(&spi->dev);
+
+ wm8711_unregister(wm8711);
+
+ return 0;
+}
+
+static struct spi_driver wm8711_spi_driver = {
+ .driver = {
+ .name = "wm8711",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8711_spi_probe,
+ .remove = __devexit_p(wm8711_spi_remove),
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8711_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8711_priv *wm8711;
+ struct snd_soc_codec *codec;
+
+ wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+ if (wm8711 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8711->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(i2c, wm8711);
+ codec->control_data = i2c;
+
+ codec->dev = &i2c->dev;
+
+ return wm8711_register(wm8711, SND_SOC_I2C);
+}
+
+static __devexit int wm8711_i2c_remove(struct i2c_client *client)
+{
+ struct wm8711_priv *wm8711 = i2c_get_clientdata(client);
+ wm8711_unregister(wm8711);
+ return 0;
+}
+
+static const struct i2c_device_id wm8711_i2c_id[] = {
+ { "wm8711", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
+
+static struct i2c_driver wm8711_i2c_driver = {
+ .driver = {
+ .name = "WM8711 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8711_i2c_probe,
+ .remove = __devexit_p(wm8711_i2c_remove),
+ .id_table = wm8711_i2c_id,
+};
+#endif
+
+static int __init wm8711_modinit(void)
+{
+ int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&wm8711_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8711_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return 0;
+}
+module_init(wm8711_modinit);
+
+static void __exit wm8711_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8711_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8711_spi_driver);
+#endif
+}
+module_exit(wm8711_exit);
+
+MODULE_DESCRIPTION("ASoC WM8711 driver");
+MODULE_AUTHOR("Mike Arthur");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8711.h b/sound/soc/codecs/wm8711.h
new file mode 100644
index 00000000000..381e84a4381
--- /dev/null
+++ b/sound/soc/codecs/wm8711.h
@@ -0,0 +1,42 @@
+/*
+ * wm8711.h -- WM8711 Soc Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.h
+ *
+ * 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.
+ */
+
+#ifndef _WM8711_H
+#define _WM8711_H
+
+/* WM8711 register space */
+
+#define WM8711_LOUT1V 0x02
+#define WM8711_ROUT1V 0x03
+#define WM8711_APANA 0x04
+#define WM8711_APDIGI 0x05
+#define WM8711_PWR 0x06
+#define WM8711_IFACE 0x07
+#define WM8711_SRATE 0x08
+#define WM8711_ACTIVE 0x09
+#define WM8711_RESET 0x0f
+
+#define WM8711_CACHEREGNUM 8
+
+#define WM8711_SYSCLK 0
+#define WM8711_DAI 0
+
+struct wm8711_setup_data {
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8711_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8711;
+
+#endif
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index d3fd4f28d96..bb95af95097 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -19,6 +19,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -33,9 +34,18 @@
static struct snd_soc_codec *wm8731_codec;
struct snd_soc_codec_device soc_codec_dev_wm8731;
+#define WM8731_NUM_SUPPLIES 4
+static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
+ "AVDD",
+ "HPVDD",
+ "DCVDD",
+ "DBVDD",
+};
+
/* codec private data */
struct wm8731_priv {
struct snd_soc_codec codec;
+ struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
u16 reg_cache[WM8731_CACHEREGNUM];
unsigned int sysclk;
};
@@ -422,9 +432,12 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
+ struct wm8731_priv *wm8731 = codec->private_data;
snd_soc_write(codec, WM8731_ACTIVE, 0x0);
wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
return 0;
}
@@ -432,10 +445,16 @@ static int wm8731_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
- int i;
+ struct wm8731_priv *wm8731 = codec->private_data;
+ int i, ret;
u8 data[2];
u16 *cache = codec->reg_cache;
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0)
+ return ret;
+
/* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
@@ -444,6 +463,7 @@ static int wm8731_resume(struct platform_device *pdev)
}
wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8731_set_bias_level(codec, codec->suspend_bias_level);
+
return 0;
}
#else
@@ -512,7 +532,7 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
static int wm8731_register(struct wm8731_priv *wm8731,
enum snd_soc_control_type control)
{
- int ret;
+ int ret, i;
struct snd_soc_codec *codec = &wm8731->codec;
if (wm8731_codec) {
@@ -543,10 +563,27 @@ static int wm8731_register(struct wm8731_priv *wm8731,
goto err;
}
+ for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
+ wm8731->supplies[i].supply = wm8731_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+ wm8731->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_regulator_get;
+ }
+
ret = wm8731_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
- goto err;
+ goto err_regulator_enable;
}
wm8731_dai.dev = codec->dev;
@@ -567,7 +604,7 @@ static int wm8731_register(struct wm8731_priv *wm8731,
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
- goto err;
+ goto err_regulator_enable;
}
ret = snd_soc_register_dai(&wm8731_dai);
@@ -581,6 +618,10 @@ static int wm8731_register(struct wm8731_priv *wm8731,
err_codec:
snd_soc_unregister_codec(codec);
+err_regulator_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+err_regulator_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
err:
kfree(wm8731);
return ret;
@@ -591,6 +632,8 @@ static void wm8731_unregister(struct wm8731_priv *wm8731)
wm8731_set_bias_level(&wm8731->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&wm8731_dai);
snd_soc_unregister_codec(&wm8731->codec);
+ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+ regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
kfree(wm8731);
wm8731_codec = NULL;
}
@@ -623,21 +666,6 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
- return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8731_spi_resume(struct spi_device *spi)
-{
- return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8731_spi_suspend NULL
-#define wm8731_spi_resume NULL
-#endif
-
static struct spi_driver wm8731_spi_driver = {
.driver = {
.name = "wm8731",
@@ -645,8 +673,6 @@ static struct spi_driver wm8731_spi_driver = {
.owner = THIS_MODULE,
},
.probe = wm8731_spi_probe,
- .suspend = wm8731_spi_suspend,
- .resume = wm8731_spi_resume,
.remove = __devexit_p(wm8731_spi_remove),
};
#endif /* CONFIG_SPI_MASTER */
@@ -679,21 +705,6 @@ static __devexit int wm8731_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
- return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8731_i2c_resume(struct i2c_client *i2c)
-{
- return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8731_i2c_suspend NULL
-#define wm8731_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8731_i2c_id[] = {
{ "wm8731", 0 },
{ }
@@ -707,8 +718,6 @@ static struct i2c_driver wm8731_i2c_driver = {
},
.probe = wm8731_i2c_probe,
.remove = __devexit_p(wm8731_i2c_remove),
- .suspend = wm8731_i2c_suspend,
- .resume = wm8731_i2c_resume,
.id_table = wm8731_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 5ad677ce80d..8f7305257d2 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -724,8 +724,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
pll_div->k = K;
}
-static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
u16 reg, enable;
int offset;
@@ -1767,21 +1767,6 @@ static int wm8753_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8753_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8753_i2c_suspend NULL
-#define wm8753_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8753_i2c_id[] = {
{ "wm8753", 0 },
{ }
@@ -1795,8 +1780,6 @@ static struct i2c_driver wm8753_i2c_driver = {
},
.probe = wm8753_i2c_probe,
.remove = wm8753_i2c_remove,
- .suspend = wm8753_i2c_suspend,
- .resume = wm8753_i2c_resume,
.id_table = wm8753_i2c_id,
};
#endif
@@ -1852,22 +1835,6 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
- return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8753_spi_resume(struct spi_device *spi)
-{
- return snd_soc_resume_device(&spi->dev);
-}
-
-#else
-#define wm8753_spi_suspend NULL
-#define wm8753_spi_resume NULL
-#endif
-
static struct spi_driver wm8753_spi_driver = {
.driver = {
.name = "wm8753",
@@ -1876,8 +1843,6 @@ static struct spi_driver wm8753_spi_driver = {
},
.probe = wm8753_spi_probe,
.remove = __devexit_p(wm8753_spi_remove),
- .suspend = wm8753_spi_suspend,
- .resume = wm8753_spi_resume,
};
#endif
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index a9829aa26e5..a0bbb28eed7 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -616,21 +616,6 @@ static int __devexit wm8776_spi_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8776_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
- return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8776_spi_resume(struct spi_device *spi)
-{
- return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8776_spi_suspend NULL
-#define wm8776_spi_resume NULL
-#endif
-
static struct spi_driver wm8776_spi_driver = {
.driver = {
.name = "wm8776",
@@ -638,8 +623,6 @@ static struct spi_driver wm8776_spi_driver = {
.owner = THIS_MODULE,
},
.probe = wm8776_spi_probe,
- .suspend = wm8776_spi_suspend,
- .resume = wm8776_spi_resume,
.remove = __devexit_p(wm8776_spi_remove),
};
#endif /* CONFIG_SPI_MASTER */
@@ -673,21 +656,6 @@ static __devexit int wm8776_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8776_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
- return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8776_i2c_resume(struct i2c_client *i2c)
-{
- return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8776_i2c_suspend NULL
-#define wm8776_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8776_i2c_id[] = {
{ "wm8776", 0 },
{ }
@@ -701,8 +669,6 @@ static struct i2c_driver wm8776_i2c_driver = {
},
.probe = wm8776_i2c_probe,
.remove = __devexit_p(wm8776_i2c_remove),
- .suspend = wm8776_i2c_suspend,
- .resume = wm8776_i2c_resume,
.id_table = wm8776_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 5e9c855c003..b48804b5cac 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -814,8 +814,8 @@ reenable:
return 0;
}
-static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out);
}
@@ -1312,21 +1312,6 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8900_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8900_i2c_suspend NULL
-#define wm8900_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8900_i2c_id[] = {
{ "wm8900", 0 },
{ }
@@ -1340,8 +1325,6 @@ static struct i2c_driver wm8900_i2c_driver = {
},
.probe = wm8900_i2c_probe,
.remove = __devexit_p(wm8900_i2c_remove),
- .suspend = wm8900_i2c_suspend,
- .resume = wm8900_i2c_resume,
.id_table = wm8900_i2c_id,
};
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index fe1307b500c..94cdb813041 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1655,21 +1655,6 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8903_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8903_i2c_suspend NULL
-#define wm8903_i2c_resume NULL
-#endif
-
/* i2c codec control layer */
static const struct i2c_device_id wm8903_i2c_id[] = {
{ "wm8903", 0 },
@@ -1684,8 +1669,6 @@ static struct i2c_driver wm8903_i2c_driver = {
},
.probe = wm8903_i2c_probe,
.remove = __devexit_p(wm8903_i2c_remove),
- .suspend = wm8903_i2c_suspend,
- .resume = wm8903_i2c_resume,
.id_table = wm8903_i2c_id,
};
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 1ef2454c520..8d4fd3c08c0 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -536,8 +536,8 @@ static void pll_factors(unsigned int target, unsigned int source)
}
/* Untested at the moment */
-static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
@@ -877,21 +877,6 @@ static int __devexit wm8940_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8940_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8940_i2c_suspend NULL
-#define wm8940_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8940_i2c_id[] = {
{ "wm8940", 0 },
{ }
@@ -905,8 +890,6 @@ static struct i2c_driver wm8940_i2c_driver = {
},
.probe = wm8940_i2c_probe,
.remove = __devexit_p(wm8940_i2c_remove),
- .suspend = wm8940_i2c_suspend,
- .resume = wm8940_i2c_resume,
.id_table = wm8940_i2c_id,
};
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index f59703be61c..b9b096a8539 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -540,8 +540,8 @@ static int pll_factors(unsigned int source, unsigned int target,
return 0;
}
-static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
@@ -883,21 +883,6 @@ static __devexit int wm8960_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8960_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8960_i2c_suspend NULL
-#define wm8960_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8960_i2c_id[] = {
{ "wm8960", 0 },
{ }
@@ -911,8 +896,6 @@ static struct i2c_driver wm8960_i2c_driver = {
},
.probe = wm8960_i2c_probe,
.remove = __devexit_p(wm8960_i2c_remove),
- .suspend = wm8960_i2c_suspend,
- .resume = wm8960_i2c_resume,
.id_table = wm8960_i2c_id,
};
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 50303208589..b5c6f2cd5ae 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -1206,21 +1206,6 @@ static __devexit int wm8961_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8961_i2c_suspend(struct i2c_client *client, pm_message_t state)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8961_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8961_i2c_suspend NULL
-#define wm8961_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8961_i2c_id[] = {
{ "wm8961", 0 },
{ }
@@ -1234,8 +1219,6 @@ static struct i2c_driver wm8961_i2c_driver = {
},
.probe = wm8961_i2c_probe,
.remove = __devexit_p(wm8961_i2c_remove),
- .suspend = wm8961_i2c_suspend,
- .resume = wm8961_i2c_resume,
.id_table = wm8961_i2c_id,
};
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 98d663afc97..eff29331235 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -281,36 +281,38 @@ static int wm8974_add_widgets(struct snd_soc_codec *codec)
}
struct pll_ {
- unsigned int pre_div:4; /* prescale - 1 */
+ unsigned int pre_div:1;
unsigned int n:4;
unsigned int k;
};
-static struct pll_ pll_div;
-
/* The size in bits of the pll divide multiplied by 10
* to allow rounding later */
#define FIXED_PLL_SIZE ((1 << 24) * 10)
-static void pll_factors(unsigned int target, unsigned int source)
+static void pll_factors(struct pll_ *pll_div,
+ unsigned int target, unsigned int source)
{
unsigned long long Kpart;
unsigned int K, Ndiv, Nmod;
+ /* There is a fixed divide by 4 in the output path */
+ target *= 4;
+
Ndiv = target / source;
if (Ndiv < 6) {
- source >>= 1;
- pll_div.pre_div = 1;
+ source /= 2;
+ pll_div->pre_div = 1;
Ndiv = target / source;
} else
- pll_div.pre_div = 0;
+ pll_div->pre_div = 0;
if ((Ndiv < 6) || (Ndiv > 12))
printk(KERN_WARNING
"WM8974 N value %u outwith recommended range!\n",
Ndiv);
- pll_div.n = Ndiv;
+ pll_div->n = Ndiv;
Nmod = target % source;
Kpart = FIXED_PLL_SIZE * (long long)Nmod;
@@ -325,13 +327,14 @@ static void pll_factors(unsigned int target, unsigned int source)
/* Move down to proper range now rounding is done */
K /= 10;
- pll_div.k = K;
+ pll_div->k = K;
}
-static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
+ struct pll_ pll_div;
u16 reg;
if (freq_in == 0 || freq_out == 0) {
@@ -345,7 +348,7 @@ static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
return 0;
}
- pll_factors(freq_out*4, freq_in);
+ pll_factors(&pll_div, freq_out, freq_in);
snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 3f530f8a972..d8d8f68b81e 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -944,21 +944,6 @@ static int wm8988_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8988_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8988_i2c_suspend NULL
-#define wm8988_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm8988_i2c_id[] = {
{ "wm8988", 0 },
{ }
@@ -972,8 +957,6 @@ static struct i2c_driver wm8988_i2c_driver = {
},
.probe = wm8988_i2c_probe,
.remove = wm8988_i2c_remove,
- .suspend = wm8988_i2c_suspend,
- .resume = wm8988_i2c_resume,
.id_table = wm8988_i2c_id,
};
#endif
@@ -1006,21 +989,6 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM
-static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
- return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8988_spi_resume(struct spi_device *spi)
-{
- return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8988_spi_suspend NULL
-#define wm8988_spi_resume NULL
-#endif
-
static struct spi_driver wm8988_spi_driver = {
.driver = {
.name = "wm8988",
@@ -1029,8 +997,6 @@ static struct spi_driver wm8988_spi_driver = {
},
.probe = wm8988_spi_probe,
.remove = __devexit_p(wm8988_spi_remove),
- .suspend = wm8988_spi_suspend,
- .resume = wm8988_spi_resume,
};
#endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 2d702db4131..f657e9a5fe2 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -972,8 +972,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
pll_div->k = K;
}
-static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
u16 reg;
struct snd_soc_codec *codec = codec_dai->codec;
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index d9987999e92..dac39771214 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -422,7 +422,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
-static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id,
+static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
struct snd_soc_codec *codec = dai->codec;
@@ -1572,33 +1572,15 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
/* Use automatic clock configuration */
snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
- if (!wm8993->pdata.lineout1_diff)
- snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
- WM8993_LINEOUT1_MODE,
- WM8993_LINEOUT1_MODE);
- if (!wm8993->pdata.lineout2_diff)
- snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
- WM8993_LINEOUT2_MODE,
- WM8993_LINEOUT2_MODE);
-
- if (wm8993->pdata.lineout1fb)
- snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
- WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
-
- if (wm8993->pdata.lineout2fb)
- snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
- WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
-
- /* Apply the microphone bias/detection configuration - the
- * platform data is directly applicable to the register. */
- snd_soc_update_bits(codec, WM8993_MICBIAS,
- WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
- WM8993_MICB1_LVL | WM8993_MICB2_LVL,
- wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT |
- wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT |
- wm8993->pdata.micbias1_lvl |
- wm8993->pdata.micbias1_lvl << 1);
-
+ wm_hubs_handle_analogue_pdata(codec, wm8993->pdata.lineout1_diff,
+ wm8993->pdata.lineout2_diff,
+ wm8993->pdata.lineout1fb,
+ wm8993->pdata.lineout2fb,
+ wm8993->pdata.jd_scthr,
+ wm8993->pdata.jd_thr,
+ wm8993->pdata.micbias1_lvl,
+ wm8993->pdata.micbias2_lvl);
+
ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (ret != 0)
goto err;
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 686e5aa9720..4cb6b104b72 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1452,21 +1452,6 @@ static __devexit int wm9081_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
- return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm9081_i2c_resume(struct i2c_client *client)
-{
- return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm9081_i2c_suspend NULL
-#define wm9081_i2c_resume NULL
-#endif
-
static const struct i2c_device_id wm9081_i2c_id[] = {
{ "wm9081", 0 },
{ }
@@ -1480,8 +1465,6 @@ static struct i2c_driver wm9081_i2c_driver = {
},
.probe = wm9081_i2c_probe,
.remove = __devexit_p(wm9081_i2c_remove),
- .suspend = wm9081_i2c_suspend,
- .resume = wm9081_i2c_resume,
.id_table = wm9081_i2c_id,
};
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index abed37acf78..ca3d449ed89 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -800,8 +800,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
return 0;
}
-static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index e542027eea8..810a563d0eb 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -738,6 +738,41 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
+int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
+ int lineout1_diff, int lineout2_diff,
+ int lineout1fb, int lineout2fb,
+ int jd_scthr, int jd_thr, int micbias1_lvl,
+ int micbias2_lvl)
+{
+ if (!lineout1_diff)
+ snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+ WM8993_LINEOUT1_MODE,
+ WM8993_LINEOUT1_MODE);
+ if (!lineout2_diff)
+ snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+ WM8993_LINEOUT2_MODE,
+ WM8993_LINEOUT2_MODE);
+
+ if (lineout1fb)
+ snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
+
+ if (lineout2fb)
+ snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
+
+ snd_soc_update_bits(codec, WM8993_MICBIAS,
+ WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
+ WM8993_MICB1_LVL | WM8993_MICB2_LVL,
+ jd_scthr << WM8993_JD_SCTHR_SHIFT |
+ jd_thr << WM8993_JD_THR_SHIFT |
+ micbias1_lvl |
+ micbias2_lvl << WM8993_MICB2_LVL_SHIFT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
+
MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index ec09cb6a293..36d3fba1de8 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -20,5 +20,10 @@ extern const unsigned int wm_hubs_spkmix_tlv[];
extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
+extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
+ int lineout1_diff, int lineout2_diff,
+ int lineout1fb, int lineout2fb,
+ int jd_scthr, int jd_thr,
+ int micbias1_lvl, int micbias2_lvl);
#endif
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 4dfd4ad9d90..047ee39418c 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -13,9 +13,9 @@ config SND_DAVINCI_SOC_MCASP
tristate
config SND_DAVINCI_SOC_EVM
- tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
+ tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
depends on SND_DAVINCI_SOC
- depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM
+ depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM
select SND_DAVINCI_SOC_I2S
select SND_SOC_TLV320AIC3X
help
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 67414f65940..7ccbe6684fc 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -45,7 +45,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
unsigned sysclk;
/* ASP1 on DM355 EVM is clocked by an external oscillator */
- if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
+ if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() ||
+ machine_is_davinci_dm365_evm())
sysclk = 27000000;
/* ASP0 in DM6446 EVM is clocked by U55, as configured by
@@ -176,7 +177,7 @@ static struct snd_soc_dai_link da8xx_evm_dai = {
.ops = &evm_ops,
};
-/* davinci-evm audio machine driver */
+/* davinci dm6446, dm355 or dm365 evm audio machine driver */
static struct snd_soc_card snd_soc_card_evm = {
.name = "DaVinci EVM",
.platform = &davinci_soc_platform,
@@ -243,7 +244,7 @@ static int __init evm_init(void)
int index;
int ret;
- if (machine_is_davinci_evm()) {
+ if (machine_is_davinci_evm() || machine_is_davinci_dm365_evm()) {
evm_snd_dev_data = &evm_snd_devdata;
index = 0;
} else if (machine_is_davinci_dm355_evm()) {
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index 4ae70704802..2ab809359c0 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -397,6 +397,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
}
dma_params->acnt = dma_params->data_type;
+ dma_params->fifo_level = 0;
+
rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 5d1f98a4c97..50ad0519a8f 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -714,16 +714,13 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
struct davinci_pcm_dma_params *dma_params =
&dev->dma_params[substream->stream];
int word_length;
- u8 numevt;
+ u8 fifo_level;
davinci_hw_common_param(dev, substream->stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- numevt = dev->txnumevt;
+ fifo_level = dev->txnumevt;
else
- numevt = dev->rxnumevt;
-
- if (!numevt)
- numevt = 1;
+ fifo_level = dev->rxnumevt;
if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
davinci_hw_dit_param(dev);
@@ -751,12 +748,12 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- if (dev->version == MCASP_VERSION_2) {
- dma_params->data_type *= numevt;
- dma_params->acnt = 4 * numevt;
- } else
+ if (dev->version == MCASP_VERSION_2 && !fifo_level)
+ dma_params->acnt = 4;
+ else
dma_params->acnt = dma_params->data_type;
+ dma_params->fifo_level = fifo_level;
davinci_config_channel_size(dev, word_length);
return 0;
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index c73a915f233..fb10f1d63fd 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -66,38 +66,53 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
dma_addr_t dma_pos;
dma_addr_t src, dst;
unsigned short src_bidx, dst_bidx;
+ unsigned short src_cidx, dst_cidx;
unsigned int data_type;
unsigned short acnt;
unsigned int count;
+ unsigned int fifo_level;
period_size = snd_pcm_lib_period_bytes(substream);
dma_offset = prtd->period * period_size;
dma_pos = runtime->dma_addr + dma_offset;
+ fifo_level = prtd->params->fifo_level;
pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
"dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
data_type = prtd->params->data_type;
count = period_size / data_type;
+ if (fifo_level)
+ count /= fifo_level;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
src = dma_pos;
dst = prtd->params->dma_addr;
src_bidx = data_type;
dst_bidx = 0;
+ src_cidx = data_type * fifo_level;
+ dst_cidx = 0;
} else {
src = prtd->params->dma_addr;
dst = dma_pos;
src_bidx = 0;
dst_bidx = data_type;
+ src_cidx = 0;
+ dst_cidx = data_type * fifo_level;
}
acnt = prtd->params->acnt;
edma_set_src(lch, src, INCR, W8BIT);
edma_set_dest(lch, dst, INCR, W8BIT);
- edma_set_src_index(lch, src_bidx, 0);
- edma_set_dest_index(lch, dst_bidx, 0);
- edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
+
+ edma_set_src_index(lch, src_bidx, src_cidx);
+ edma_set_dest_index(lch, dst_bidx, dst_cidx);
+
+ if (!fifo_level)
+ edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
+ else
+ edma_set_transfer_params(lch, acnt, fifo_level, count,
+ fifo_level, ABSYNC);
prtd->period++;
if (unlikely(prtd->period >= runtime->periods))
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index 8746606efc8..c8b0d2baf05 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -23,6 +23,7 @@ struct davinci_pcm_dma_params {
enum dma_event_q eventq_no; /* event queue number */
unsigned char data_type; /* xfer data type */
unsigned char convert_mono_stereo;
+ unsigned int fifo_level;
};
diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c
index e4dcb539108..0267d2d9168 100644
--- a/sound/soc/imx/mx27vis_wm8974.c
+++ b/sound/soc/imx/mx27vis_wm8974.c
@@ -157,7 +157,7 @@ static int mx27vis_hifi_hw_params(struct snd_pcm_substream *substream,
/* codec PLL input is 25 MHz */
- ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG,
+ ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG,
25000000, pll_out);
if (ret < 0) {
printk(KERN_ERR "Error when setting PLL input\n");
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 653a362425d..bb5731a22be 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -66,6 +66,15 @@ config SND_OMAP_SOC_OMAP3EVM
help
Say Y if you want to add support for SoC audio on the omap3evm board.
+config SND_OMAP_SOC_AM3517EVM
+ tristate "SoC Audio support for OMAP3517 / AM3517 EVM"
+ depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TLV320AIC23
+ help
+ Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517
+ EVM.
+
config SND_OMAP_SOC_SDP3430
tristate "SoC Audio support for Texas Instruments SDP3430"
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 02d69471dcb..0c78ae4e6b9 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -12,6 +12,7 @@ snd-soc-osk5912-objs := osk5912.o
snd-soc-overo-objs := overo.o
snd-soc-omap2evm-objs := omap2evm.o
snd-soc-omap3evm-objs := omap3evm.o
+snd-soc-am3517evm-objs := am3517evm.o
snd-soc-sdp3430-objs := sdp3430.o
snd-soc-omap3pandora-objs := omap3pandora.o
snd-soc-omap3beagle-objs := omap3beagle.o
@@ -23,6 +24,7 @@ obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o
+obj-$(CONFIG_MACH_OMAP3517EVM) += snd-soc-am3517evm.o
obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c
new file mode 100644
index 00000000000..135901b2ea1
--- /dev/null
+++ b/sound/soc/omap/am3517evm.c
@@ -0,0 +1,202 @@
+/*
+ * am3517evm.c -- ALSA SoC support for OMAP3517 / AM3517 EVM
+ *
+ * Author: Anuj Aggarwal <anuj.aggarwal@ti.com>
+ *
+ * Based on sound/soc/omap/beagle.c by Steve Sakoman
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; 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.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <plat/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+
+#include "../codecs/tlv320aic23.h"
+
+#define CODEC_CLOCK 12000000
+
+static int am3517evm_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->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_DSP_B |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_DSP_B |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+ CODEC_CLOCK, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_CLKR_SRC_CLKX, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_CLKR_SRC_CLKX\n");
+ return ret;
+ }
+
+ snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_FSR_SRC_FSX\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops am3517evm_ops = {
+ .hw_params = am3517evm_hw_params,
+};
+
+/* am3517evm machine dapm widgets */
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Line Out", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_MIC("Mic In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Line Out connected to LLOUT, RLOUT */
+ {"Line Out", NULL, "LOUT"},
+ {"Line Out", NULL, "ROUT"},
+
+ {"LLINEIN", NULL, "Line In"},
+ {"RLINEIN", NULL, "Line In"},
+
+ {"MICIN", NULL, "Mic In"},
+};
+
+static int am3517evm_aic23_init(struct snd_soc_codec *codec)
+{
+ /* Add am3517-evm specific widgets */
+ snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+ ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+ /* Set up davinci-evm specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ /* always connected */
+ snd_soc_dapm_enable_pin(codec, "Line Out");
+ snd_soc_dapm_enable_pin(codec, "Line In");
+ snd_soc_dapm_enable_pin(codec, "Mic In");
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link am3517evm_dai = {
+ .name = "TLV320AIC23",
+ .stream_name = "AIC23",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &tlv320aic23_dai,
+ .init = am3517evm_aic23_init,
+ .ops = &am3517evm_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_am3517evm = {
+ .name = "am3517evm",
+ .platform = &omap_soc_platform,
+ .dai_link = &am3517evm_dai,
+ .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device am3517evm_snd_devdata = {
+ .card = &snd_soc_am3517evm,
+ .codec_dev = &soc_codec_dev_tlv320aic23,
+};
+
+static struct platform_device *am3517evm_snd_device;
+
+static int __init am3517evm_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_omap3517evm()) {
+ pr_err("Not OMAP3517 / AM3517 EVM!\n");
+ return -ENODEV;
+ }
+ pr_info("OMAP3517 / AM3517 EVM SoC init\n");
+
+ am3517evm_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!am3517evm_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(am3517evm_snd_device, &am3517evm_snd_devdata);
+ am3517evm_snd_devdata.dev = &am3517evm_snd_device->dev;
+ *(unsigned int *)am3517evm_dai.cpu_dai->private_data = 0; /* McBSP1 */
+
+ ret = platform_device_add(am3517evm_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(am3517evm_snd_device);
+
+ return ret;
+}
+
+static void __exit am3517evm_soc_exit(void)
+{
+ platform_device_unregister(am3517evm_snd_device);
+}
+
+module_init(am3517evm_soc_init);
+module_exit(am3517evm_soc_exit);
+
+MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3517 / AM3517 EVM");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c
index 5a5166ac727..ae0fc9b135d 100644
--- a/sound/soc/omap/ams-delta.c
+++ b/sound/soc/omap/ams-delta.c
@@ -40,7 +40,7 @@
/* Board specific DAPM widgets */
- const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
/* Handset */
SND_SOC_DAPM_MIC("Mouthpiece", NULL),
SND_SOC_DAPM_HP("Earpiece", NULL),
@@ -81,7 +81,7 @@ static const char *ams_delta_audio_mode[] =
(1 << AMS_DELTA_SPEAKER))
#define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC))
-unsigned short ams_delta_audio_mode_pins[] = {
+static const unsigned short ams_delta_audio_mode_pins[] = {
AMS_DELTA_MIXED,
AMS_DELTA_HANDSET,
AMS_DELTA_HANDSFREE,
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 5735945788b..6a829eef2a4 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -195,8 +195,12 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
else
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
- omap_set_dma_src_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16);
- omap_set_dma_dest_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16);
+ if (!(cpu_class_is_omap1())) {
+ omap_set_dma_src_burst_mode(prtd->dma_ch,
+ OMAP_DMA_DATA_BURST_16);
+ omap_set_dma_dest_burst_mode(prtd->dma_ch,
+ OMAP_DMA_DATA_BURST_16);
+ }
return 0;
}
diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c
index 9114c263077..8deb59bb10b 100644
--- a/sound/soc/omap/omap3evm.c
+++ b/sound/soc/omap/omap3evm.c
@@ -93,10 +93,17 @@ static struct snd_soc_card snd_soc_omap3evm = {
.num_links = 1,
};
+/* twl4030 setup */
+static struct twl4030_setup_data twl4030_setup = {
+ .ramp_delay_value = 4,
+ .sysclk = 26000,
+};
+
/* Audio subsystem */
static struct snd_soc_device omap3evm_snd_devdata = {
.card = &snd_soc_omap3evm,
.codec_dev = &soc_codec_dev_twl4030,
+ .codec_data = &twl4030_setup,
};
static struct platform_device *omap3evm_snd_device;
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index dcb3181bb34..d4f4031afa3 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -90,7 +90,8 @@ config SND_PXA2XX_SOC_E800
config SND_PXA2XX_SOC_EM_X270
tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300"
- depends on SND_PXA2XX_SOC && MACH_EM_X270
+ depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \
+ MACH_CM_X300)
select SND_PXA2XX_SOC_AC97
select SND_SOC_WM9712
help
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
index 9f7c61e23da..4c8d99a8d38 100644
--- a/sound/soc/pxa/magician.c
+++ b/sound/soc/pxa/magician.c
@@ -213,7 +213,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
return ret;
/* set SSP audio pll clock */
- ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, acps);
+ ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps);
if (ret < 0)
return ret;
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index d11a6d7e384..3bd7712f029 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -305,8 +305,8 @@ static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
/*
* Configure the PLL frequency pxa27x and (afaik - pxa320 only)
*/
-static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
{
struct ssp_priv *priv = cpu_dai->private_data;
struct ssp_device *ssp = priv->dev.ssp;
@@ -760,13 +760,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
.resume = pxa_ssp_resume,
.playback = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
.capture = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
@@ -780,13 +780,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
.resume = pxa_ssp_resume,
.playback = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
.capture = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
@@ -801,13 +801,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
.resume = pxa_ssp_resume,
.playback = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
.capture = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
@@ -822,13 +822,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
.resume = pxa_ssp_resume,
.playback = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
.capture = {
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 8,
.rates = PXA_SSP_RATES,
.formats = PXA_SSP_FORMATS,
},
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index 9a386b4c4ed..dd678ae2439 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -74,7 +74,8 @@ static const struct snd_soc_dapm_route audio_map[] = {
static int zylonite_wm9713_init(struct snd_soc_codec *codec)
{
if (clk_pout)
- snd_soc_dai_set_pll(&codec->dai[0], 0, clk_get_rate(pout), 0);
+ snd_soc_dai_set_pll(&codec->dai[0], 0, 0,
+ clk_get_rate(pout), 0);
snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
ARRAY_SIZE(zylonite_dapm_widgets));
@@ -128,7 +129,7 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
+ ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out);
if (ret < 0)
return ret;
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 923428fc1ad..d7912f1e462 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -56,6 +56,15 @@ config SND_S3C24XX_SOC_JIVE_WM8750
help
Sat Y if you want to add support for SoC audio on the Jive.
+config SND_S3C64XX_SOC_WM8580
+ tristate "SoC I2S Audio support for WM8580 on SMDK64XX"
+ depends on SND_S3C24XX_SOC && (MACH_SMDK6400 || MACH_SMDK6410)
+ depends on BROKEN
+ select SND_SOC_WM8580
+ select SND_S3C64XX_SOC_I2S
+ help
+ Sat Y if you want to add support for SoC audio on the SMDK64XX.
+
config SND_S3C24XX_SOC_SMDK2443_WM9710
tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
depends on SND_S3C24XX_SOC && MACH_SMDK2443
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 99f5a7dd3fc..7790406f90b 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -23,6 +23,7 @@ snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
+snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o
obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -33,4 +34,5 @@ obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
+obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
index 0c52e36ddd8..26409a9cef9 100644
--- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
@@ -119,7 +119,7 @@ static int neo1973_gta02_hifi_hw_params(struct snd_pcm_substream *substream,
return ret;
/* codec PLL input is PCLK/4 */
- ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
iis_clkrate / 4, pll_out);
if (ret < 0)
return ret;
@@ -133,7 +133,7 @@ static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
/* disable the PLL */
- return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
}
/*
@@ -183,7 +183,7 @@ static int neo1973_gta02_voice_hw_params(
return ret;
/* configue and enable PLL for 12.288MHz output */
- ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
iis_clkrate / 4, 12288000);
if (ret < 0)
return ret;
@@ -197,7 +197,7 @@ static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
/* disable the PLL */
- return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
}
static struct snd_soc_ops neo1973_gta02_voice_ops = {
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 906709e6dd5..77de6c5127d 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -29,7 +29,6 @@
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
-#include <plat/audio.h>
#include <linux/io.h>
#include <mach/spi-gpio.h>
@@ -137,7 +136,7 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
return ret;
/* codec PLL input is PCLK/4 */
- ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
iis_clkrate / 4, pll_out);
if (ret < 0)
return ret;
@@ -153,7 +152,7 @@ static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
pr_debug("Entered %s\n", __func__);
/* disable the PLL */
- return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
}
/*
@@ -203,7 +202,7 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
return ret;
/* configue and enable PLL for 12.288MHz output */
- ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
iis_clkrate / 4, 12288000);
if (ret < 0)
return ret;
@@ -219,7 +218,7 @@ static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
pr_debug("Entered %s\n", __func__);
/* disable the PLL */
- return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
}
static struct snd_soc_ops neo1973_voice_ops = {
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c
index 9bc4aa35caa..28b0ab25509 100644
--- a/sound/soc/s3c24xx/s3c-i2s-v2.c
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.c
@@ -32,7 +32,6 @@
#include <plat/regs-s3c2412-iis.h>
-#include <plat/audio.h>
#include <mach/dma.h>
#include "s3c-i2s-v2.h"
@@ -312,12 +311,15 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
+ iismod |= S3C2412_IISMOD_LR_RLOW;
iismod |= S3C2412_IISMOD_SDF_MSB;
break;
case SND_SOC_DAIFMT_LEFT_J:
+ iismod |= S3C2412_IISMOD_LR_RLOW;
iismod |= S3C2412_IISMOD_SDF_LSB;
break;
case SND_SOC_DAIFMT_I2S:
+ iismod &= ~S3C2412_IISMOD_LR_RLOW;
iismod |= S3C2412_IISMOD_SDF_IIS;
break;
default:
@@ -467,6 +469,31 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
switch (div_id) {
case S3C_I2SV2_DIV_BCLK:
+ if (div > 3) {
+ /* convert value to bit field */
+
+ switch (div) {
+ case 16:
+ div = S3C2412_IISMOD_BCLK_16FS;
+ break;
+
+ case 32:
+ div = S3C2412_IISMOD_BCLK_32FS;
+ break;
+
+ case 24:
+ div = S3C2412_IISMOD_BCLK_24FS;
+ break;
+
+ case 48:
+ div = S3C2412_IISMOD_BCLK_48FS;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
reg = readl(i2s->regs + S3C2412_IISMOD);
reg &= ~S3C2412_IISMOD_BCLK_MASK;
writel(reg | div, i2s->regs + S3C2412_IISMOD);
@@ -626,7 +653,7 @@ int s3c_i2sv2_probe(struct platform_device *pdev,
}
i2s->iis_pclk = clk_get(dev, "iis");
- if (i2s->iis_pclk == NULL) {
+ if (IS_ERR(i2s->iis_pclk)) {
dev_err(dev, "failed to get iis_clock\n");
iounmap(i2s->regs);
return -ENOENT;
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index a587ec40b44..ac5e47b082f 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -34,7 +34,6 @@
#include <plat/regs-s3c2412-iis.h>
-#include <plat/audio.h>
#include <mach/regs-gpio.h>
#include <mach/dma.h>
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index fc1beb0930b..b25e9f968df 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -32,7 +32,6 @@
#include <plat/regs-ac97.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
-#include <plat/audio.h>
#include <asm/dma.h>
#include <mach/dma.h>
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index 40e2c4790f0..c76b8bb214b 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -32,7 +32,7 @@
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
-#include <plat/audio.h>
+
#include <asm/dma.h>
#include <mach/dma.h>
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index 5cbbdc80fde..27cf097c2b1 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -29,7 +29,6 @@
#include <asm/dma.h>
#include <mach/hardware.h>
#include <mach/dma.h>
-#include <plat/audio.h>
#include "s3c24xx-pcm.h"
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c
index 3c06c401d0f..b67eed59666 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.c
@@ -31,7 +31,6 @@
#include <plat/gpio-bank-d.h>
#include <plat/gpio-bank-e.h>
#include <plat/gpio-cfg.h>
-#include <plat/audio.h>
#include <mach/map.h>
#include <mach/dma.h>
@@ -99,6 +98,19 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
iismod |= S3C64XX_IISMOD_IMS_SYSMUX;
break;
+ case S3C64XX_CLKSRC_CDCLK:
+ switch (dir) {
+ case SND_SOC_CLOCK_IN:
+ iismod |= S3C64XX_IISMOD_CDCLKCON;
+ break;
+ case SND_SOC_CLOCK_OUT:
+ iismod &= ~S3C64XX_IISMOD_CDCLKCON;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
default:
return -EINVAL;
}
@@ -111,8 +123,12 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai)
{
struct s3c_i2sv2_info *i2s = to_info(dai);
+ u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
- return i2s->iis_cclk;
+ if (iismod & S3C64XX_IISMOD_IMS_SYSMUX)
+ return i2s->iis_cclk;
+ else
+ return i2s->iis_pclk;
}
EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock);
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h
index 02148cee261..abe7253b55f 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.h
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.h
@@ -25,6 +25,7 @@ struct clk;
#define S3C64XX_CLKSRC_PCLK (0)
#define S3C64XX_CLKSRC_MUX (1)
+#define S3C64XX_CLKSRC_CDCLK (2)
extern struct snd_soc_dai s3c64xx_i2s_dai[];
diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c
new file mode 100644
index 00000000000..cb8a9161b64
--- /dev/null
+++ b/sound/soc/s3c24xx/smdk64xx_wm8580.c
@@ -0,0 +1,268 @@
+/*
+ * smdk64xx_wm8580.c
+ *
+ * Copyright (c) 2009 Samsung Electronics Co. Ltd
+ * Author: Jaswinder Singh <jassi.brar@samsung.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm8580.h"
+#include "s3c24xx-pcm.h"
+#include "s3c64xx-i2s.h"
+
+#define S3C64XX_I2S_V4 2
+
+/* SMDK64XX has a 12MHZ crystal attached to WM8580 */
+#define SMDK64XX_WM8580_FREQ 12000000
+
+static int smdk64xx_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 *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ unsigned int pll_out;
+ int bfs, rfs, ret;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ bfs = 16;
+ break;
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bfs = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* The Fvco for WM8580 PLLs must fall within [90,100]MHz.
+ * This criterion can't be met if we request PLL output
+ * as {8000x256, 64000x256, 11025x256}Hz.
+ * As a wayout, we rather change rfs to a minimum value that
+ * results in (params_rate(params) * rfs), and itself, acceptable
+ * to both - the CODEC and the CPU.
+ */
+ switch (params_rate(params)) {
+ case 16000:
+ case 22050:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ rfs = 256;
+ break;
+ case 64000:
+ rfs = 384;
+ break;
+ case 8000:
+ case 11025:
+ rfs = 512;
+ break;
+ default:
+ return -EINVAL;
+ }
+ pll_out = params_rate(params) * rfs;
+
+ /* Set the Codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ /* Set the AP DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_CDCLK,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* We use PCLK for basic ops in SoC-Slave mode */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* Set WM8580 to drive MCLK from its PLLA */
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+ WM8580_CLKSRC_PLLA);
+ if (ret < 0)
+ return ret;
+
+ /* Explicitly set WM8580-DAC to source from MCLK */
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL,
+ WM8580_CLKSRC_MCLK);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, WM8580_PLLA,
+ SMDK64XX_WM8580_FREQ, pll_out);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_BCLK, bfs);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, rfs);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * SMDK64XX WM8580 DAI operations.
+ */
+static struct snd_soc_ops smdk64xx_ops = {
+ .hw_params = smdk64xx_hw_params,
+};
+
+/* SMDK64xx Playback widgets */
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets_pbk[] = {
+ SND_SOC_DAPM_HP("Front-L/R", NULL),
+ SND_SOC_DAPM_HP("Center/Sub", NULL),
+ SND_SOC_DAPM_HP("Rear-L/R", NULL),
+};
+
+/* SMDK64xx Capture widgets */
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets_cpt[] = {
+ SND_SOC_DAPM_MIC("MicIn", NULL),
+ SND_SOC_DAPM_LINE("LineIn", NULL),
+};
+
+/* SMDK-PAIFTX connections */
+static const struct snd_soc_dapm_route audio_map_tx[] = {
+ /* MicIn feeds AINL */
+ {"AINL", NULL, "MicIn"},
+
+ /* LineIn feeds AINL/R */
+ {"AINL", NULL, "LineIn"},
+ {"AINR", NULL, "LineIn"},
+};
+
+/* SMDK-PAIFRX connections */
+static const struct snd_soc_dapm_route audio_map_rx[] = {
+ /* Front Left/Right are fed VOUT1L/R */
+ {"Front-L/R", NULL, "VOUT1L"},
+ {"Front-L/R", NULL, "VOUT1R"},
+
+ /* Center/Sub are fed VOUT2L/R */
+ {"Center/Sub", NULL, "VOUT2L"},
+ {"Center/Sub", NULL, "VOUT2R"},
+
+ /* Rear Left/Right are fed VOUT3L/R */
+ {"Rear-L/R", NULL, "VOUT3L"},
+ {"Rear-L/R", NULL, "VOUT3R"},
+};
+
+static int smdk64xx_wm8580_init_paiftx(struct snd_soc_codec *codec)
+{
+ /* Add smdk64xx specific Capture widgets */
+ snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_cpt,
+ ARRAY_SIZE(wm8580_dapm_widgets_cpt));
+
+ /* Set up PAIFTX audio path */
+ snd_soc_dapm_add_routes(codec, audio_map_tx, ARRAY_SIZE(audio_map_tx));
+
+ /* Enabling the microphone requires the fitting of a 0R
+ * resistor to connect the line from the microphone jack.
+ */
+ snd_soc_dapm_disable_pin(codec, "MicIn");
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static int smdk64xx_wm8580_init_paifrx(struct snd_soc_codec *codec)
+{
+ /* Add smdk64xx specific Playback widgets */
+ snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_pbk,
+ ARRAY_SIZE(wm8580_dapm_widgets_pbk));
+
+ /* Set up PAIFRX audio path */
+ snd_soc_dapm_add_routes(codec, audio_map_rx, ARRAY_SIZE(audio_map_rx));
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link smdk64xx_dai[] = {
+{ /* Primary Playback i/f */
+ .name = "WM8580 PAIF RX",
+ .stream_name = "Playback",
+ .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4],
+ .codec_dai = &wm8580_dai[WM8580_DAI_PAIFRX],
+ .init = smdk64xx_wm8580_init_paifrx,
+ .ops = &smdk64xx_ops,
+},
+{ /* Primary Capture i/f */
+ .name = "WM8580 PAIF TX",
+ .stream_name = "Capture",
+ .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4],
+ .codec_dai = &wm8580_dai[WM8580_DAI_PAIFTX],
+ .init = smdk64xx_wm8580_init_paiftx,
+ .ops = &smdk64xx_ops,
+},
+};
+
+static struct snd_soc_card smdk64xx = {
+ .name = "smdk64xx",
+ .platform = &s3c24xx_soc_platform,
+ .dai_link = smdk64xx_dai,
+ .num_links = ARRAY_SIZE(smdk64xx_dai),
+};
+
+static struct snd_soc_device smdk64xx_snd_devdata = {
+ .card = &smdk64xx,
+ .codec_dev = &soc_codec_dev_wm8580,
+};
+
+static struct platform_device *smdk64xx_snd_device;
+
+static int __init smdk64xx_audio_init(void)
+{
+ int ret;
+
+ smdk64xx_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!smdk64xx_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(smdk64xx_snd_device, &smdk64xx_snd_devdata);
+ smdk64xx_snd_devdata.dev = &smdk64xx_snd_device->dev;
+ ret = platform_device_add(smdk64xx_snd_device);
+
+ if (ret)
+ platform_device_put(smdk64xx_snd_device);
+
+ return ret;
+}
+module_init(smdk64xx_audio_init);
+
+MODULE_AUTHOR("Jaswinder Singh, jassi.brar@samsung.com");
+MODULE_DESCRIPTION("ALSA SoC SMDK64XX WM8580");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 9154b4363db..9e697658655 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -23,7 +23,6 @@ config SND_SOC_SH4_SSI
config SND_SOC_SH4_FSI
tristate "SH4 FSI support"
depends on CPU_SUBTYPE_SH7724
- select SH_DMA
help
This option enables FSI sound support
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 44123248b63..e1a3d1a2b4c 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -26,8 +26,6 @@
#include <sound/pcm_params.h>
#include <sound/sh_fsi.h>
#include <asm/atomic.h>
-#include <asm/dma.h>
-#include <asm/dma-sh.h>
#define DO_FMT 0x0000
#define DOFF_CTL 0x0004
@@ -97,7 +95,6 @@ struct fsi_priv {
int fifo_max;
int chan;
- int dma_chan;
int byte_offset;
int period_len;
@@ -308,62 +305,6 @@ static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play)
return residue;
}
-static int fsi_get_residue(struct fsi_priv *fsi, int is_play)
-{
- int residue;
- int width;
- struct snd_pcm_runtime *runtime;
-
- runtime = fsi->substream->runtime;
-
- /* get 1 channel data width */
- width = frames_to_bytes(runtime, 1) / fsi->chan;
-
- if (2 == width)
- residue = fsi_get_fifo_residue(fsi, is_play);
- else
- residue = get_dma_residue(fsi->dma_chan);
-
- return residue;
-}
-
-/************************************************************************
-
-
- basic dma function
-
-
-************************************************************************/
-#define PORTA_DMA 0
-#define PORTB_DMA 1
-
-static int fsi_get_dma_chan(void)
-{
- if (0 != request_dma(PORTA_DMA, "fsia"))
- return -EIO;
-
- if (0 != request_dma(PORTB_DMA, "fsib")) {
- free_dma(PORTA_DMA);
- return -EIO;
- }
-
- master->fsia.dma_chan = PORTA_DMA;
- master->fsib.dma_chan = PORTB_DMA;
-
- return 0;
-}
-
-static void fsi_free_dma_chan(void)
-{
- dma_wait_for_completion(PORTA_DMA);
- dma_wait_for_completion(PORTB_DMA);
- free_dma(PORTA_DMA);
- free_dma(PORTB_DMA);
-
- master->fsia.dma_chan = -1;
- master->fsib.dma_chan = -1;
-}
-
/************************************************************************
@@ -435,44 +376,6 @@ static void fsi_soft_all_reset(void)
mdelay(10);
}
-static void fsi_16data_push(struct fsi_priv *fsi,
- struct snd_pcm_runtime *runtime,
- int send)
-{
- u16 *dma_start;
- u32 snd;
- int i;
-
- /* get dma start position for FSI */
- dma_start = (u16 *)runtime->dma_area;
- dma_start += fsi->byte_offset / 2;
-
- /*
- * soft dma
- * FSI can not use DMA when 16bpp
- */
- for (i = 0; i < send; i++) {
- snd = (u32)dma_start[i];
- fsi_reg_write(fsi, DODT, snd << 8);
- }
-}
-
-static void fsi_32data_push(struct fsi_priv *fsi,
- struct snd_pcm_runtime *runtime,
- int send)
-{
- u32 *dma_start;
-
- /* get dma start position for FSI */
- dma_start = (u32 *)runtime->dma_area;
- dma_start += fsi->byte_offset / 4;
-
- dma_wait_for_completion(fsi->dma_chan);
- dma_configure_channel(fsi->dma_chan, (SM_INC|0x400|TS_32|TM_BUR));
- dma_write(fsi->dma_chan, (u32)dma_start,
- (u32)(fsi->base + DODT), send * 4);
-}
-
/* playback interrupt */
static int fsi_data_push(struct fsi_priv *fsi)
{
@@ -481,6 +384,8 @@ static int fsi_data_push(struct fsi_priv *fsi)
int send;
int fifo_free;
int width;
+ u8 *start;
+ int i;
if (!fsi ||
!fsi->substream ||
@@ -515,12 +420,22 @@ static int fsi_data_push(struct fsi_priv *fsi)
if (fifo_free < send)
send = fifo_free;
- if (2 == width)
- fsi_16data_push(fsi, runtime, send);
- else if (4 == width)
- fsi_32data_push(fsi, runtime, send);
- else
+ start = runtime->dma_area;
+ start += fsi->byte_offset;
+
+ switch (width) {
+ case 2:
+ for (i = 0; i < send; i++)
+ fsi_reg_write(fsi, DODT,
+ ((u32)*((u16 *)start + i) << 8));
+ break;
+ case 4:
+ for (i = 0; i < send; i++)
+ fsi_reg_write(fsi, DODT, *((u32 *)start + i));
+ break;
+ default:
return -EINVAL;
+ }
fsi->byte_offset += send * width;
@@ -532,6 +447,75 @@ static int fsi_data_push(struct fsi_priv *fsi)
return 0;
}
+static int fsi_data_pop(struct fsi_priv *fsi)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_pcm_substream *substream = NULL;
+ int free;
+ int fifo_fill;
+ int width;
+ u8 *start;
+ int i;
+
+ if (!fsi ||
+ !fsi->substream ||
+ !fsi->substream->runtime)
+ return -EINVAL;
+
+ runtime = fsi->substream->runtime;
+
+ /* FSI FIFO has limit.
+ * So, this driver can not send periods data at a time
+ */
+ if (fsi->byte_offset >=
+ fsi->period_len * (fsi->periods + 1)) {
+
+ substream = fsi->substream;
+ fsi->periods = (fsi->periods + 1) % runtime->periods;
+
+ if (0 == fsi->periods)
+ fsi->byte_offset = 0;
+ }
+
+ /* get 1 channel data width */
+ width = frames_to_bytes(runtime, 1) / fsi->chan;
+
+ /* get free space for alsa */
+ free = (fsi->buffer_len - fsi->byte_offset) / width;
+
+ /* get recv size */
+ fifo_fill = fsi_get_fifo_residue(fsi, 0);
+
+ if (free < fifo_fill)
+ fifo_fill = free;
+
+ start = runtime->dma_area;
+ start += fsi->byte_offset;
+
+ switch (width) {
+ case 2:
+ for (i = 0; i < fifo_fill; i++)
+ *((u16 *)start + i) =
+ (u16)(fsi_reg_read(fsi, DIDT) >> 8);
+ break;
+ case 4:
+ for (i = 0; i < fifo_fill; i++)
+ *((u32 *)start + i) = fsi_reg_read(fsi, DIDT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fsi->byte_offset += fifo_fill * width;
+
+ fsi_irq_enable(fsi, 0);
+
+ if (substream)
+ snd_pcm_period_elapsed(substream);
+
+ return 0;
+}
+
static irqreturn_t fsi_interrupt(int irq, void *data)
{
u32 status = fsi_master_read(SOFT_RST) & ~0x00000010;
@@ -545,6 +529,10 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
fsi_data_push(&master->fsia);
if (int_st & INT_B_OUT)
fsi_data_push(&master->fsib);
+ if (int_st & INT_A_IN)
+ fsi_data_pop(&master->fsia);
+ if (int_st & INT_B_IN)
+ fsi_data_pop(&master->fsib);
fsi_master_write(INT_ST, 0x0000000);
@@ -664,8 +652,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
}
fsi_reg_write(fsi, reg, data);
- dev_dbg(dai->dev, "use %s format (%d channel) use %d DMAC\n",
- msg, fsi->chan, fsi->dma_chan);
/*
* clear clk reset if master mode
@@ -699,16 +685,12 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int ret = 0;
- /* capture not supported */
- if (!is_play)
- return -ENODEV;
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
fsi_stream_push(fsi, substream,
frames_to_bytes(runtime, runtime->buffer_size),
frames_to_bytes(runtime, runtime->period_size));
- ret = fsi_data_push(fsi);
+ ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
break;
case SNDRV_PCM_TRIGGER_STOP:
fsi_irq_disable(fsi, is_play);
@@ -780,10 +762,9 @@ static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsi_priv *fsi = fsi_get(substream);
- int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
long location;
- location = (fsi->byte_offset - 1) - fsi_get_residue(fsi, is_play);
+ location = (fsi->byte_offset - 1);
if (location < 0)
location = 0;
@@ -845,7 +826,12 @@ struct snd_soc_dai fsi_soc_dai[] = {
.channels_min = 1,
.channels_max = 8,
},
- /* capture not supported */
+ .capture = {
+ .rates = FSI_RATES,
+ .formats = FSI_FMTS,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
.ops = &fsi_dai_ops,
},
{
@@ -857,7 +843,12 @@ struct snd_soc_dai fsi_soc_dai[] = {
.channels_min = 1,
.channels_max = 8,
},
- /* capture not supported */
+ .capture = {
+ .rates = FSI_RATES,
+ .formats = FSI_FMTS,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
.ops = &fsi_dai_ops,
},
};
@@ -912,22 +903,13 @@ static int fsi_probe(struct platform_device *pdev)
master->fsia.base = master->base;
master->fsib.base = master->base + 0x40;
- master->fsia.dma_chan = -1;
- master->fsib.dma_chan = -1;
-
- ret = fsi_get_dma_chan();
- if (ret < 0) {
- dev_err(&pdev->dev, "cannot get dma api\n");
- goto exit_iounmap;
- }
-
/* FSI is based on SPU mstp */
snprintf(clk_name, sizeof(clk_name), "spu%d", pdev->id);
master->clk = clk_get(NULL, clk_name);
if (IS_ERR(master->clk)) {
dev_err(&pdev->dev, "cannot get %s mstp\n", clk_name);
ret = -EIO;
- goto exit_free_dma;
+ goto exit_iounmap;
}
fsi_soc_dai[0].dev = &pdev->dev;
@@ -938,7 +920,7 @@ static int fsi_probe(struct platform_device *pdev)
ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master);
if (ret) {
dev_err(&pdev->dev, "irq request err\n");
- goto exit_free_dma;
+ goto exit_iounmap;
}
ret = snd_soc_register_platform(&fsi_soc_platform);
@@ -951,8 +933,6 @@ static int fsi_probe(struct platform_device *pdev)
exit_free_irq:
free_irq(irq, master);
-exit_free_dma:
- fsi_free_dma_chan();
exit_iounmap:
iounmap(master->base);
exit_kfree:
@@ -969,8 +949,6 @@ static int fsi_remove(struct platform_device *pdev)
clk_put(master->clk);
- fsi_free_dma_chan();
-
free_irq(master->irq, master);
iounmap(master->base);
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index c8ceddc2a26..d2505e8b06c 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -77,6 +77,35 @@ static int snd_soc_7_9_spi_write(void *control_data, const char *data,
#define snd_soc_7_9_spi_write NULL
#endif
+static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 *cache = codec->reg_cache;
+ u8 data[2];
+
+ BUG_ON(codec->volatile_register);
+
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ if (reg < codec->reg_cache_size)
+ cache[reg] = value;
+
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 *cache = codec->reg_cache;
+ if (reg >= codec->reg_cache_size)
+ return -1;
+ return cache[reg];
+}
+
static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
@@ -150,9 +179,20 @@ static struct {
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
} io_types[] = {
- { 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read },
- { 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read,
- snd_soc_8_16_read_i2c },
+ {
+ .addr_bits = 7, .data_bits = 9,
+ .write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
+ .spi_write = snd_soc_7_9_spi_write
+ },
+ {
+ .addr_bits = 8, .data_bits = 8,
+ .write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
+ },
+ {
+ .addr_bits = 8, .data_bits = 16,
+ .write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
+ .i2c_read = snd_soc_8_16_read_i2c,
+ },
};
/**
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 0a1b2f64bbe..2d190df9fcc 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -790,45 +790,6 @@ static int soc_resume(struct device *dev)
return 0;
}
-
-/**
- * snd_soc_suspend_device: Notify core of device suspend
- *
- * @dev: Device being suspended.
- *
- * In order to ensure that the entire audio subsystem is suspended in a
- * coordinated fashion ASoC devices should suspend themselves when
- * called by ASoC. When the standard kernel suspend process asks the
- * device to suspend it should call this function to initiate a suspend
- * of the entire ASoC card.
- *
- * \note Currently this function is stubbed out.
- */
-int snd_soc_suspend_device(struct device *dev)
-{
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_suspend_device);
-
-/**
- * snd_soc_resume_device: Notify core of device resume
- *
- * @dev: Device being resumed.
- *
- * In order to ensure that the entire audio subsystem is resumed in a
- * coordinated fashion ASoC devices should resume themselves when called
- * by ASoC. When the standard kernel resume process asks the device
- * to resume it should call this function. Once all the components of
- * the card have notified that they are ready to be resumed the card
- * will be resumed.
- *
- * \note Currently this function is stubbed out.
- */
-int snd_soc_resume_device(struct device *dev)
-{
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_resume_device);
#else
#define soc_suspend NULL
#define soc_resume NULL
@@ -1262,21 +1223,39 @@ static const struct file_operations codec_reg_fops = {
static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
{
+ char codec_root[128];
+
+ if (codec->dev)
+ snprintf(codec_root, sizeof(codec_root),
+ "%s.%s", codec->name, dev_name(codec->dev));
+ else
+ snprintf(codec_root, sizeof(codec_root),
+ "%s", codec->name);
+
+ codec->debugfs_codec_root = debugfs_create_dir(codec_root,
+ debugfs_root);
+ if (!codec->debugfs_codec_root) {
+ printk(KERN_WARNING
+ "ASoC: Failed to create codec debugfs directory\n");
+ return;
+ }
+
codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
- debugfs_root, codec,
- &codec_reg_fops);
+ codec->debugfs_codec_root,
+ codec, &codec_reg_fops);
if (!codec->debugfs_reg)
printk(KERN_WARNING
"ASoC: Failed to create codec register debugfs file\n");
codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
- debugfs_root,
+ codec->debugfs_codec_root,
&codec->pop_time);
if (!codec->debugfs_pop_time)
printk(KERN_WARNING
"Failed to create pop time debugfs file\n");
- codec->debugfs_dapm = debugfs_create_dir("dapm", debugfs_root);
+ codec->debugfs_dapm = debugfs_create_dir("dapm",
+ codec->debugfs_codec_root);
if (!codec->debugfs_dapm)
printk(KERN_WARNING
"Failed to create DAPM debugfs directory\n");
@@ -1286,9 +1265,7 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
{
- debugfs_remove_recursive(codec->debugfs_dapm);
- debugfs_remove(codec->debugfs_pop_time);
- debugfs_remove(codec->debugfs_reg);
+ debugfs_remove_recursive(codec->debugfs_codec_root);
}
#else
@@ -2205,16 +2182,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
* snd_soc_dai_set_pll - configure DAI PLL.
* @dai: DAI
* @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
* @freq_in: PLL input clock frequency in Hz
* @freq_out: requested PLL output clock frequency in Hz
*
* Configures and enables PLL to generate output clock based on input clock.
*/
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out)
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
{
if (dai->ops && dai->ops->set_pll)
- return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);
+ return dai->ops->set_pll(dai, pll_id, source,
+ freq_in, freq_out);
else
return -EINVAL;
}
@@ -2259,6 +2238,30 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
/**
+ * snd_soc_dai_set_channel_map - configure DAI audio channel map
+ * @dai: DAI
+ * @tx_num: how many TX channels
+ * @tx_slot: pointer to an array which imply the TX slot number channel
+ * 0~num-1 uses
+ * @rx_num: how many RX channels
+ * @rx_slot: pointer to an array which imply the RX slot number channel
+ * 0~num-1 uses
+ *
+ * configure the relationship between channel number and TDM slot number.
+ */
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ if (dai->ops && dai->ops->set_channel_map)
+ return dai->ops->set_channel_map(dai, tx_num, tx_slot,
+ rx_num, rx_slot);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
+
+/**
* snd_soc_dai_set_tristate - configure DAI system or master clock.
* @dai: DAI
* @tristate: tristate enable
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index d89f6dc0090..eaadb4b742f 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -719,6 +719,10 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
/* Check if one of our outputs is connected */
list_for_each_entry(path, &w->sinks, list_source) {
+ if (path->connected &&
+ !path->connected(path->source, path->sink))
+ continue;
+
if (path->sink && path->sink->power_check &&
path->sink->power_check(path->sink)) {
power = 1;
@@ -1138,6 +1142,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
w->active ? "active" : "inactive");
list_for_each_entry(p, &w->sources, list_sink) {
+ if (p->connected && !p->connected(w, p->sink))
+ continue;
+
if (p->connect)
ret += snprintf(buf + ret, PAGE_SIZE - ret,
" in %s %s\n",
@@ -1145,6 +1152,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
p->source->name);
}
list_for_each_entry(p, &w->sinks, list_source) {
+ if (p->connected && !p->connected(w, p->sink))
+ continue;
+
if (p->connect)
ret += snprintf(buf + ret, PAGE_SIZE - ret,
" out %s %s\n",
@@ -1192,8 +1202,8 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec)
/* test and update the power status of a mux widget */
static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
- struct snd_kcontrol *kcontrol, int mask,
- int mux, int val, struct soc_enum *e)
+ struct snd_kcontrol *kcontrol, int change,
+ int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 0;
@@ -1202,7 +1212,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
widget->id != snd_soc_dapm_value_mux)
return -ENODEV;
- if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
+ if (!change)
return 0;
/* find dapm widget path assoc with kcontrol */
@@ -1387,10 +1397,13 @@ int snd_soc_dapm_sync(struct snd_soc_codec *codec)
EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
- const char *sink, const char *control, const char *source)
+ const struct snd_soc_dapm_route *route)
{
struct snd_soc_dapm_path *path;
struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
+ const char *sink = route->sink;
+ const char *control = route->control;
+ const char *source = route->source;
int ret = 0;
/* find src and dest widgets */
@@ -1414,6 +1427,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
path->source = wsource;
path->sink = wsink;
+ path->connected = route->connected;
INIT_LIST_HEAD(&path->list);
INIT_LIST_HEAD(&path->list_source);
INIT_LIST_HEAD(&path->list_sink);
@@ -1514,8 +1528,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
int i, ret;
for (i = 0; i < num; i++) {
- ret = snd_soc_dapm_add_route(codec, route->sink,
- route->control, route->source);
+ ret = snd_soc_dapm_add_route(codec, route);
if (ret < 0) {
printk(KERN_ERR "Failed to add route %s->%s\n",
route->source,
@@ -1752,7 +1765,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int val, mux;
+ unsigned int val, mux, change;
unsigned int mask, bitmask;
int ret = 0;
@@ -1772,20 +1785,21 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock(&widget->codec->mutex);
widget->value = val;
- dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
- if (widget->event) {
- if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
- ret = widget->event(widget,
- kcontrol, SND_SOC_DAPM_PRE_REG);
- if (ret < 0)
- goto out;
- }
- ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
- if (widget->event_flags & SND_SOC_DAPM_POST_REG)
- ret = widget->event(widget,
- kcontrol, SND_SOC_DAPM_POST_REG);
- } else
- ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+ change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+ if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+ ret = widget->event(widget,
+ kcontrol, SND_SOC_DAPM_PRE_REG);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+ if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+ ret = widget->event(widget,
+ kcontrol, SND_SOC_DAPM_POST_REG);
out:
mutex_unlock(&widget->codec->mutex);
@@ -1794,6 +1808,54 @@ out:
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
/**
+ * snd_soc_dapm_get_enum_virt - Get virtual DAPM mux
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = widget->value;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
+
+/**
+ * snd_soc_dapm_put_enum_virt - Set virtual DAPM mux
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e =
+ (struct soc_enum *)kcontrol->private_value;
+ int change;
+ int ret = 0;
+
+ if (ucontrol->value.enumerated.item[0] >= e->max)
+ return -EINVAL;
+
+ mutex_lock(&widget->codec->mutex);
+
+ change = widget->value != ucontrol->value.enumerated.item[0];
+ widget->value = ucontrol->value.enumerated.item[0];
+ dapm_mux_update_power(widget, kcontrol, change, widget->value, e);
+
+ mutex_unlock(&widget->codec->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
+
+/**
* snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get
* callback
* @kcontrol: mixer control
@@ -1851,7 +1913,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int val, mux;
+ unsigned int val, mux, change;
unsigned int mask;
int ret = 0;
@@ -1869,20 +1931,21 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock(&widget->codec->mutex);
widget->value = val;
- dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
- if (widget->event) {
- if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
- ret = widget->event(widget,
- kcontrol, SND_SOC_DAPM_PRE_REG);
- if (ret < 0)
- goto out;
- }
- ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
- if (widget->event_flags & SND_SOC_DAPM_POST_REG)
- ret = widget->event(widget,
- kcontrol, SND_SOC_DAPM_POST_REG);
- } else
- ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+ change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+ dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+ if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+ ret = widget->event(widget,
+ kcontrol, SND_SOC_DAPM_PRE_REG);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+ if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+ ret = widget->event(widget,
+ kcontrol, SND_SOC_DAPM_POST_REG);
out:
mutex_unlock(&widget->codec->mutex);
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 1d455ab7949..12124149601 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -58,7 +58,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_new);
*/
void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
{
- struct snd_soc_codec *codec = jack->card->codec;
+ struct snd_soc_codec *codec;
struct snd_soc_jack_pin *pin;
int enable;
int oldstatus;
@@ -67,6 +67,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
WARN_ON_ONCE(!jack);
return;
}
+ codec = jack->card->codec;
mutex_lock(&codec->mutex);