aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/Kconfig24
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/gtm601.c97
-rw-r--r--sound/soc/codecs/gtm601.h23
-rw-r--r--sound/soc/codecs/si47xx.c523
-rw-r--r--sound/soc/codecs/si47xx.h31
-rw-r--r--sound/soc/codecs/twl4030.c6
-rw-r--r--sound/soc/codecs/twl4030.h53
-rw-r--r--sound/soc/codecs/w2cbw003-bt.c102
-rw-r--r--sound/soc/codecs/w2cbw003-bt.h23
-rw-r--r--sound/soc/omap/Kconfig8
-rw-r--r--sound/soc/omap/Makefile3
-rw-r--r--sound/soc/omap/gta04-audio.c370
-rw-r--r--sound/soc/omap/gta04-fm.c157
-rw-r--r--sound/soc/omap/gta04-headset.c146
-rw-r--r--sound/soc/omap/gta04-jack.c230
-rw-r--r--sound/soc/omap/gta04-voice.c159
-rw-r--r--sound/soc/omap/omap-twl4030.c7
18 files changed, 1972 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index badb6fb..0fbab4e 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -127,6 +127,9 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
select SND_SOC_WM9713 if SND_SOC_AC97_BUS
+ select SND_SOC_GTM601
+ select SND_SOC_SI47XX if I2C
+ select SND_SOC_W2CBW003
help
Normally ASoC codec drivers are only built if a machine driver which
uses them is also built since they are only usable with a machine
@@ -533,3 +536,24 @@ config SND_SOC_ML26124
config SND_SOC_TPA6130A2
tristate
+
+config SND_SOC_GTM601
+ tristate
+ help
+ PCM interface to a GTM601 UMTS modem chip. Does not control the Modem through USB.
+
+config SND_SOC_WM2000
+ tristate
+
+config SND_SOC_WM9090
+ tristate
+
+config SND_SOC_SI47XX
+ tristate
+ help
+ PCM interface to a Si47xx chip. Needs to enable some I2C or SPI driver.
+
+config SND_SOC_W2CBW003
+ tristate
+ help
+ PCM interface to Bluetooth part of W2CBW003. Does not control the bluetooth headset link.
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 70fd806..091be2c 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -124,6 +124,11 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
+snd-soc-wm2000-objs := wm2000.o
+snd-soc-wm9090-objs := wm9090.o
+snd-soc-gtm601-objs := gtm601.o
+snd-soc-si47xx-objs := si47xx.o
+snd-soc-w2cbw003-bt-objs := w2cbw003-bt.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
@@ -250,3 +255,8 @@ 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
+obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
+obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
+obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
+#obj-$(CONFIG_SND_SOC_SI47XX) += snd-soc-si47xx.o
+obj-$(CONFIG_SND_SOC_W2CBW003) += snd-soc-w2cbw003-bt.o
diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c
new file mode 100644
index 0000000..289b25d
--- /dev/null
+++ b/sound/soc/codecs/gtm601.c
@@ -0,0 +1,97 @@
+/*
+ * FIXME: this is a blueprint for the GTM601 Voice PCM interface
+ * needs to be adapted for GTA04
+ * CHECKME: can we use the generic AC97 or SPDIF driver instead of defining our own?
+ *
+ * gtm601.c
+ *
+ * Created on: 15-Oct-2009
+ * Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies Ltd.
+ *
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "gtm601.h"
+/*
+ * Note this is a simple chip with no configuration interface, sample rate is
+ * determined automatically by examining the Master clock and Bit clock ratios
+ */
+
+#define GTM601_RATES (SNDRV_PCM_RATE_8000) // CHECKME
+
+struct snd_soc_dai_driver gtm601_dai = {
+ .name = "GTM601",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1, // CHECKME
+ .channels_max = 1,
+ .rates = GTM601_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, /* this is the only format the
+ * omap-mcbsp-dai understands */
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = GTM601_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+struct snd_soc_codec_driver soc_codec_dev_gtm601;
+
+static int gtm601_platform_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_gtm601, &gtm601_dai, 1);
+}
+
+static int gtm601_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+MODULE_ALIAS("platform:gtm601_codec_audio");
+
+static struct platform_driver gtm601_codec_driver = {
+ .driver = {
+ .name = "gtm601_codec_audio",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = gtm601_platform_probe,
+ .remove = gtm601_platform_remove,
+};
+
+static int __init gtm601_init(void)
+{
+ return platform_driver_register(&gtm601_codec_driver);
+}
+module_init(gtm601_init);
+
+static void __exit gtm601_exit(void)
+{
+ platform_driver_unregister(&gtm601_codec_driver);
+}
+module_exit(gtm601_exit);
+
+MODULE_DESCRIPTION("ASoC GTM601 driver");
+MODULE_AUTHOR("Neil Jones");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/gtm601.h b/sound/soc/codecs/gtm601.h
new file mode 100644
index 0000000..b8fcf4f
--- /dev/null
+++ b/sound/soc/codecs/gtm601.h
@@ -0,0 +1,23 @@
+/*
+ * gtm601.h
+ *
+ * Created on: 15-Oct-2009
+ * Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies Ltd.
+ *
+ * Adapted on: 20-Aug-2011
+ *
+ * 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 GTM601_H_
+#define GTM601_H_
+
+extern struct snd_soc_dai_driver gtm601_dai;
+extern struct snd_soc_codec_driver soc_codec_dev_gtm601;
+
+#endif /* GTM601_H_ */
diff --git a/sound/soc/codecs/si47xx.c b/sound/soc/codecs/si47xx.c
new file mode 100644
index 0000000..23d5bf6
--- /dev/null
+++ b/sound/soc/codecs/si47xx.c
@@ -0,0 +1,523 @@
+/*
+ * FIXME: this is a blueprint for a Si47xx I2C driver
+ * it is based on the WM8728 driver and
+ * needs to be adapted for GTA04
+ *
+ * si47xx.c -- Si47xx ALSA SoC Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
+ */
+
+#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/initval.h>
+#include <sound/tlv.h>
+
+#include "si47xx.h"
+
+struct si47xx_priv {
+ enum snd_soc_control_type control_type;
+};
+
+
+/*
+ * We can't read the WM8728 register space so we cache them instead.
+ * Note that the defaults here aren't the physical defaults, we latch
+ * the volume update bits, mute the output and enable infinite zero
+ * detect.
+ */
+static const u16 si47xx_reg_defaults[] = {
+ 0x1ff,
+ 0x1ff,
+ 0x001,
+ 0x100,
+};
+
+static const DECLARE_TLV_DB_SCALE(si47xx_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new si47xx_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Digital Playback Volume", SI47XX_DACLVOL, SI47XX_DACRVOL,
+ 0, 255, 0, si47xx_tlv),
+
+SOC_SINGLE("Deemphasis", SI47XX_DACCTL, 1, 1, 0),
+};
+
+/*
+ * DAPM controls.
+ */
+static const struct snd_soc_dapm_widget si47xx_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"VOUTL", NULL, "DAC"},
+ {"VOUTR", NULL, "DAC"},
+};
+
+static int si47xx_add_widgets(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ snd_soc_dapm_new_controls(dapm, si47xx_dapm_widgets,
+ ARRAY_SIZE(si47xx_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+
+ return 0;
+}
+
+static int si47xx_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, SI47XX_DACCTL);
+
+ if (mute)
+ snd_soc_write(codec, SI47XX_DACCTL, mute_reg | 1);
+ else
+ snd_soc_write(codec, SI47XX_DACCTL, mute_reg & ~1);
+
+ return 0;
+}
+
+static int si47xx_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_codec *codec = rtd->codec;
+ u16 dac = snd_soc_read(codec, SI47XX_DACCTL);
+
+ dac &= ~0x18;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ dac |= 0x10;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dac |= 0x08;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, SI47XX_DACCTL, dac);
+
+ return 0;
+}
+
+static int si47xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = snd_soc_read(codec, SI47XX_IFCTL);
+
+ /* Currently only I2S is supported by the driver, though the
+ * hardware is more flexible.
+ */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* The hardware only support full slave mode */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface &= ~0x22;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x20;
+ iface &= ~0x02;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x02;
+ iface &= ~0x20;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x22;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, SI47XX_IFCTL, iface);
+ return 0;
+}
+
+static int si47xx_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg;
+ int i;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* Power everything up... */
+ reg = snd_soc_read(codec, SI47XX_DACCTL);
+ snd_soc_write(codec, SI47XX_DACCTL, reg & ~0x4);
+
+ /* ..then sync in the register cache. */
+ for (i = 0; i < ARRAY_SIZE(si47xx_reg_defaults); i++)
+ snd_soc_write(codec, i,
+ snd_soc_read(codec, i));
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ reg = snd_soc_read(codec, SI47XX_DACCTL);
+ snd_soc_write(codec, SI47XX_DACCTL, reg | 0x4);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+// FIXME: adjust what the Si47xx chip needs...
+
+#define SI47XX_RATES (SNDRV_PCM_RATE_8000_192000)
+
+#define SI47XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops si47xx_dai_ops = {
+ .hw_params = si47xx_hw_params,
+ .digital_mute = si47xx_mute,
+ .set_fmt = si47xx_set_dai_fmt,
+};
+
+struct snd_soc_dai_driver si47xx_dai = {
+ .name = "Si47xx",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SI47XX_RATES,
+ .formats = SI47XX_FORMATS,
+ },
+ .playback = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SI47XX_RATES,
+ .formats = SI47XX_FORMATS,
+ },
+ .ops = &si47xx_dai_ops,
+};
+
+static int si47xx_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ si47xx_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int si47xx_resume(struct snd_soc_codec *codec)
+{
+ si47xx_set_bias_level(codec, codec->dapm.suspend_bias_level);
+
+ return 0;
+}
+
+struct snd_soc_codec_driver si47xx_driver = {
+ .set_bias_level = si47xx_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(si47xx_reg_defaults),
+ .reg_cache_default = si47xx_reg_defaults,
+};
+/*
+ * initialise the Si47xx driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int si47xx_init(struct snd_soc_device *socdev,
+ enum snd_soc_control_type control)
+{
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int ret = 0;
+
+ codec->name = "Si47xx";
+ codec->owner = THIS_MODULE;
+ codec->driver = si47xx_driver;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+
+ snd_soc_register_codec(dev, si47xx_driver, &si47xx_dai, 1);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ printk(KERN_ERR "si47xx: failed to configure cache I/O: %d\n",
+ ret);
+ goto err;
+ }
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "si47xx: failed to create pcms\n");
+ goto err;
+ }
+
+ /* power on device */
+ si47xx_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ snd_soc_add_controls(codec, si47xx_snd_controls,
+ ARRAY_SIZE(si47xx_snd_controls));
+ si47xx_add_widgets(codec);
+
+ return ret;
+
+err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * Si47xx 2 wire address is determined by GPIO5
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+
+static __devinit int si47xx_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct si47xx_priv *si47xx;
+ struct snd_soc_device *socdev = si47xx_socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int ret;
+
+ si47xx = kzalloc(sizeof *si47xx, GFP_KERNEL);
+ if (!si47xx)
+ return -ENOMEM;
+
+ si47xx->control_type = SND_SOC_I2C;
+ i2c_set_clientdata(i2c, si47xx);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &si47xx_driver, &si47xx_dai, 1);
+
+ if (ret < 0)
+ kfree(si47xx);
+/X/ ret = si47xx_init(socdev, SND_SOC_I2C);
+
+ if (ret < 0)
+ pr_err("failed to initialise Si47xx\n");
+ return ret;
+}
+
+static int si47xx_i2c_remove(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ snd_soc_unregister_codec(&client->dev);
+ kfree(i2c_get_clientdata(client));
+ kfree(codec->reg_cache);
+ return 0;
+}
+
+static const struct i2c_device_id si47xx_i2c_id[] = {
+ { "si47xx", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si47xx_i2c_id);
+
+static struct i2c_driver si47xx_i2c_driver = {
+ .driver = {
+ .name = "Si47xx I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = si47xx_i2c_probe,
+ .remove = si47xx_i2c_remove,
+ .id_table = si47xx_i2c_id,
+};
+
+static int si47xx_add_i2c_device(struct platform_device *pdev,
+ const struct si47xx_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&si47xx_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "si47xx", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&si47xx_i2c_driver);
+ return -ENODEV;
+}
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit si47xx_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_device *socdev = si47xx_socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int ret;
+
+ codec->control_data = spi;
+
+ ret = si47xx_init(socdev, SND_SOC_SPI);
+ if (ret < 0)
+ dev_err(&spi->dev, "failed to initialise Si47xx\n");
+
+ return ret;
+}
+
+static int __devexit si47xx_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+MODULE_ALIAS("platform:si47xx_codec_audio");
+
+static struct spi_driver si47xx_spi_driver = {
+ .driver = {
+ .name = "si47xx_codec_audio",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = si47xx_spi_probe,
+ .remove = __devexit_p(si47xx_spi_remove),
+};
+#endif /* CONFIG_SPI_MASTER */
+
+static int si47xx_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct si47xx_setup_data *setup;
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->card->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ si47xx_socdev = socdev;
+ ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ ret = si47xx_add_i2c_device(pdev, setup);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ if (setup->spi) {
+ ret = spi_register_driver(&si47xx_spi_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add spi driver");
+ }
+#endif
+
+ if (ret != 0)
+ kfree(codec);
+
+ return ret;
+}
+
+/* power down chip */
+static int si47xx_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ if (codec->control_data)
+ si47xx_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
+ i2c_del_driver(&si47xx_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&si47xx_spi_driver);
+#endif
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_si47xx = {
+ .probe = si47xx_probe,
+ .remove = si47xx_remove,
+ .suspend = si47xx_suspend,
+ .resume = si47xx_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_si47xx);
+
+static int __init si47xx_modinit(void)
+{
+ return snd_soc_register_dai(&si47xx_dai);
+}
+module_init(si47xx_modinit);
+
+static void __exit si47xx_exit(void)
+{
+ snd_soc_unregister_dai(&si47xx_dai);
+}
+module_exit(si47xx_exit);
+
+MODULE_DESCRIPTION("ASoC Si47xx driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/si47xx.h b/sound/soc/codecs/si47xx.h
new file mode 100644
index 0000000..5ebe521
--- /dev/null
+++ b/sound/soc/codecs/si47xx.h
@@ -0,0 +1,31 @@
+/*
+ * si47xx.h -- Si47xx ASoC codec driver
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * Adapted: 2011, Nikolaus Schaller <hns@goldelico.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.
+ */
+
+#ifndef _SI47XX_H
+#define _SI47XX_H
+
+#define SI47XX_DACLVOL 0x00
+#define SI47XX_DACRVOL 0x01
+#define SI47XX_DACCTL 0x02
+#define SI47XX_IFCTL 0x03
+
+struct si47xx_setup_data {
+ int spi;
+ int i2c_bus;
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_codec_device soc_codec_dev_si47xx;
+
+#endif
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 8e6e5b0..7fd64bd 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -443,6 +443,11 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
/* Make sure that the reg_cache has the same value as the HW */
twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte);
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ TWL4030_VIF_TRI_EN,
+ TWL4030_REG_VOICE_IF);
+ printk("TPS Voice IF set to tristate\n");
+
twl4030_codec_enable(codec, 0);
}
@@ -2229,6 +2234,7 @@ static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
{
struct snd_soc_codec *codec = dai->codec;
u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
+ printk("twl4030_voice_set_tristate codec=%p\n", codec);
if (tristate)
reg |= TWL4030_VIF_TRI_EN;
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
new file mode 100644
index 0000000..c5762af
--- /dev/null
+++ b/sound/soc/codecs/twl4030.h
@@ -0,0 +1,53 @@
+/*
+ * ALSA SoC TWL4030 codec driver
+ *
+ * Author: Steve Sakoman <steve@sakoman.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 __TWL4030_AUDIO_H__
+#define __TWL4030_AUDIO_H__
+
+/* Register descriptions are here */
+// #include <linux/mfd/twl4030-codec.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+/* Sgadow register used by the audio driver */
+#define TWL4030_REG_SW_SHADOW 0x4A
+#define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1)
+
+/* TWL4030_REG_SW_SHADOW (0x4A) Fields */
+#define TWL4030_HFL_EN 0x01
+#define TWL4030_HFR_EN 0x02
+
+#define TWL4030_DAI_HIFI 0
+#define TWL4030_DAI_VOICE 1
+
+extern struct snd_soc_dai twl4030_dai[2];
+extern struct snd_soc_codec_device soc_codec_dev_twl4030;
+
+struct twl4030_setup_data {
+ unsigned int ramp_delay_value;
+ unsigned int sysclk;
+ unsigned int hs_extmute:1;
+ void (*set_hs_extmute)(int mute);
+};
+
+#endif /* End of __TWL4030_AUDIO_H__ */
+
+
diff --git a/sound/soc/codecs/w2cbw003-bt.c b/sound/soc/codecs/w2cbw003-bt.c
new file mode 100644
index 0000000..b472503
--- /dev/null
+++ b/sound/soc/codecs/w2cbw003-bt.c
@@ -0,0 +1,102 @@
+/*
+ * FIXME: this is a blueprint for the W2CBW003 Bluetooth PCM interface
+ * needs to be adapted for GTA04
+ * CHECKME: can we use the generic AC97 or SPDIF driver instead of defining our own?
+ *
+ * w2cbw003.c
+ *
+ * Created on: 15-Oct-2009
+ * Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies Ltd.
+ *
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "w2cbw003-bt.h"
+/*
+ * Note this is a simple chip with no configuration interface, sample rate is
+ * determined automatically by examining the Master clock and Bit clock ratios
+ */
+
+// FIXME: adjust what the W2CBW003 PCM I/F supports...
+
+#define W2CBW003_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+
+struct snd_soc_dai_driver w2cbw003_dai = {
+ .name = "W2CBW003",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = W2CBW003_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = W2CBW003_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+};
+
+struct snd_soc_codec_driver soc_codec_dev_w2cbw003;
+
+
+static int w2cbw003_platform_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_w2cbw003, &w2cbw003_dai, 1);
+}
+
+static int w2cbw003_platform_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+MODULE_ALIAS("platform:w2cbw003_codec_audio");
+
+static struct platform_driver w2cbw003_codec_driver = {
+ .driver = {
+ .name = "w2cbw003_codec_audio",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = w2cbw003_platform_probe,
+ .remove = w2cbw003_platform_remove,
+};
+
+static int __init w2cbw003_init(void)
+{
+ return platform_driver_register(&w2cbw003_codec_driver);
+}
+module_init(w2cbw003_init);
+
+static void __exit w2cbw003_exit(void)
+{
+ platform_driver_unregister(&w2cbw003_codec_driver);
+}
+module_exit(w2cbw003_exit);
+
+MODULE_DESCRIPTION("ASoC W2CBW003 driver");
+MODULE_AUTHOR("Neil Jones");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/w2cbw003-bt.h b/sound/soc/codecs/w2cbw003-bt.h
new file mode 100644
index 0000000..27062b7
--- /dev/null
+++ b/sound/soc/codecs/w2cbw003-bt.h
@@ -0,0 +1,23 @@
+/*
+ * w2cbw003.h
+ *
+ * Created on: 15-Oct-2009
+ * Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies Ltd.
+ *
+ * Adapted on: 20-Aug-2011
+ *
+ * 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 W2CBW003_H_
+#define W2CBW003_H_
+
+extern struct snd_soc_dai_driver w2cbw003_dai;
+extern struct snd_soc_codec_driver soc_codec_dev_w2cbw003;
+
+#endif /* W2CBW003_H_ */
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 9f5d55e..365267f 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -116,3 +116,11 @@ config SND_OMAP_SOC_OMAP3_PANDORA
select SND_SOC_TWL4030
help
Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
+
+config SND_OMAP_SOC_GTA04
+ tristate "SoC Audio support for GTA04"
+ depends on TWL4030_CORE && SND_OMAP_SOC && (MACH_GTA04 || MACH_OMAP3_BEAGLE)
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y if you want to add support for SoC audio on the GTA04.
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index a725905..0e23acc 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -20,6 +20,8 @@ snd-soc-am3517evm-objs := am3517evm.o
snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o
snd-soc-omap-twl4030-objs := omap-twl4030.o
snd-soc-omap3pandora-objs := omap3pandora.o
+snd-soc-gta04-objs := gta04-voice.o gta04-headset.o gta04-jack.o
+# gta04-fm.o gta04-audio.o
snd-soc-omap-hdmi-card-objs := omap-hdmi-card.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
@@ -30,4 +32,5 @@ obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
+obj-$(CONFIG_SND_OMAP_SOC_GTA04) += snd-soc-gta04.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP_HDMI) += snd-soc-omap-hdmi-card.o
diff --git a/sound/soc/omap/gta04-audio.c b/sound/soc/omap/gta04-audio.c
new file mode 100644
index 0000000..86d4d8a
--- /dev/null
+++ b/sound/soc/omap/gta04-audio.c
@@ -0,0 +1,370 @@
+/*
+ * gta04.c -- SoC audio for GTA04 (based on OMAP3 Beagle)
+ *
+ * Author: Steve Sakoman <steve@sakoman.com>
+ * Author: Nikolaus Schaller <hns@goldelico.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/clk.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+
+#include <linux/i2c/twl4030-madc.h>
+
+#include "omap-mcbsp.h"
+#include "../codecs/twl4030.h"
+
+static int omap3gta04_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ unsigned int fmt;
+ int ret;
+
+ switch (params_channels(params)) {
+ case 2: /* Stereo I2S mode */
+ fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ case 4: /* Four channel TDM mode */
+ fmt = SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ 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, fmt);
+ 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, 26000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* this shows how we could control the AUX in/out switch or the Video in/out */
+
+static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ /*
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1);
+ gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1);
+ } else {
+ gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
+ mdelay(1);
+ gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
+ }
+ */
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget gta04_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("PCM DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,
+ 0, 0, NULL, 0, omap3pandora_hp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+ SND_SOC_DAPM_MIC("Headphone Mic", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Amplifier", NULL, "PCM DAC"},
+ {"Line Out", NULL, "PCM DAC"},
+ {"Headphone Jack", NULL, "Headphone Amplifier"},
+
+ {"AUXL", NULL, "Line In"},
+ {"AUXR", NULL, "Line In"},
+
+ /* Headset Mic: HSMIC with bias */
+ {"HSMIC", NULL, "Headset Mic Bias"},
+ {"Headset Mic Bias", NULL, "Headphone Mic"},
+
+ {"MAINMIC", NULL, "Mic Bias 1"},
+ {"Mic Bias 1", NULL, "Internal Mic"},
+ /*
+ {"SUBMIC", NULL, "Mic Bias 2"},
+ {"Mic Bias 2", NULL, "Mic (external)"},
+ */
+};
+
+static struct {
+ struct snd_soc_jack hs_jack;
+ struct delayed_work jack_work;
+ struct snd_soc_codec *codec;
+ int open;
+ /* When any jack is present, we:
+ * - poll more quickly to catch button presses
+ * - assume a 'short' is 'button press', not 'headset has
+ * no mic
+ * 'present' stores SND_JACK_HEADPHONE and SND_JACK_MICROPHONE
+ * indication what we thing is present.
+ */
+ int present;
+} jack;
+
+static void gta04_audio_jack_work(struct work_struct *work)
+{
+ long val;
+ long delay = msecs_to_jiffies(500);
+ int jackbits;
+
+ /* choose delay *before* checking presence so we still get
+ * one long delay on first insertion to help with debounce.
+ */
+ if (jack.present)
+ delay = msecs_to_jiffies(50);
+
+ val = twl4030_get_madc_conversion(7);
+ if (val < 0)
+ goto out;
+ /* On my device:
+ * open circuit = around 20
+ * short circuit = around 800
+ * microphone = around 830-840 !!!
+ */
+ if (val < 100) {
+ /* open circuit */
+ jackbits = 0;
+ jack.present = 0;
+ /* debounce */
+ delay = msecs_to_jiffies(500);
+ } else if (val < 820) {
+ /* short */
+ if (jack.present == 0) {
+ /* Inserted headset with no mic */
+ jack.present = SND_JACK_HEADPHONE;
+ jackbits = jack.present;
+ } else if (jack.present & SND_JACK_MICROPHONE) {
+ /* mic shorter == button press */
+ jackbits = SND_JACK_BTN_0 | jack.present;
+ } else {
+ /* headphones still present */
+ jackbits = jack.present;
+ }
+ } else {
+ /* There is a microphone there */
+ jack.present = SND_JACK_HEADSET;
+ jackbits = jack.present;
+ }
+ snd_soc_jack_report(&jack.hs_jack, jackbits,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+out:
+ if (jack.open)
+ schedule_delayed_work(&jack.jack_work, delay);
+}
+
+static int gta04_audio_suspend(struct snd_soc_card *card)
+{
+ if (jack.codec) {
+ snd_soc_dapm_disable_pin(&jack.codec->dapm, "Headset Mic Bias");
+ snd_soc_dapm_sync(&jack.codec->dapm);
+ }
+ return 0;
+}
+
+static int gta04_audio_resume(struct snd_soc_card *card)
+{
+ if (jack.codec && jack.open) {
+ snd_soc_dapm_force_enable_pin(&jack.codec->dapm, "Headset Mic Bias");
+ snd_soc_dapm_sync(&jack.codec->dapm);
+ }
+ return 0;
+}
+
+static int gta04_jack_open(struct input_dev *dev)
+{
+ snd_soc_dapm_force_enable_pin(&jack.codec->dapm, "Headset Mic Bias");
+ snd_soc_dapm_sync(&jack.codec->dapm);
+ jack.open = 1;
+ schedule_delayed_work(&jack.jack_work, msecs_to_jiffies(100));
+ return 0;
+}
+
+static void gta04_jack_close(struct input_dev *dev)
+{
+ jack.open = 0;
+ cancel_delayed_work_sync(&jack.jack_work);
+ snd_soc_dapm_disable_pin(&jack.codec->dapm, "Headset Mic Bias");
+ snd_soc_dapm_sync(&jack.codec->dapm);
+}
+
+static int omap3gta04_init(struct snd_soc_pcm_runtime *runtime)
+{
+ int ret;
+ struct snd_soc_codec *codec = runtime->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ ret = snd_soc_dapm_new_controls(dapm, gta04_dapm_widgets,
+ ARRAY_SIZE(gta04_dapm_widgets));
+ if (ret < 0)
+ return ret;
+
+ snd_soc_dapm_add_routes(dapm, audio_map,
+ ARRAY_SIZE(audio_map));
+
+// snd_soc_dapm_enable_pin(codec, "Ext Mic");
+// snd_soc_dapm_enable_pin(codec, "Ext Spk");
+// snd_soc_dapm_disable_pin(codec, "Headphone Mic");
+// snd_soc_dapm_disable_pin(codec, "Headphone Amplifier");
+
+ /* TWL4030 not connected pins */
+ // snd_soc_dapm_nc_pin(codec, "OUTL");
+ // snd_soc_dapm_nc_pin(codec, "OUTR");
+ // snd_soc_dapm_nc_pin(codec, "EARPIECE");
+ snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");
+ snd_soc_dapm_nc_pin(dapm, "PREDRIVER");
+ // snd_soc_dapm_nc_pin(codec, "HSOL");
+ // snd_soc_dapm_nc_pin(codec, "HSOR");
+ snd_soc_dapm_nc_pin(dapm, "CARKITMIC");
+ snd_soc_dapm_nc_pin(dapm, "CARKITL");
+ snd_soc_dapm_nc_pin(dapm, "CARKITR");
+ // snd_soc_dapm_nc_pin(codec, "HFL");
+ // snd_soc_dapm_nc_pin(codec, "HFR");
+ // snd_soc_dapm_nc_pin(codec, "VIBRA");
+ // snd_soc_dapm_nc_pin(codec, "HSMIC");
+ snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
+ snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
+
+ /* We can detect when something is plugged in,
+ * but we need to poll :-(
+ */
+ ret = snd_soc_jack_new(codec, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &jack.hs_jack);
+ if (ret)
+ return ret;
+ INIT_DELAYED_WORK(&jack.jack_work, gta04_audio_jack_work);
+ jack.codec = codec;
+ jack.hs_jack.jack->input_dev->open = gta04_jack_open;
+ jack.hs_jack.jack->input_dev->close = gta04_jack_close;
+
+ return snd_soc_dapm_sync(dapm);
+}
+
+static struct snd_soc_ops omap3gta04_ops = {
+ .hw_params = omap3gta04_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3gta04_dai = {
+ .name = "TWL4030",
+ .stream_name = "TWL4030",
+ .cpu_dai_name = "omap-mcbsp.2",
+ .platform_name = "omap-pcm-audio",
+ .codec_dai_name = "twl4030-hifi",
+ .codec_name = "twl4030-codec",
+ .dai_fmt = (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM),
+ .ops = &omap3gta04_ops,
+ .init = &omap3gta04_init
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap3gta04 = {
+ .name = "gta04",
+ .owner = THIS_MODULE,
+ .dai_link = &omap3gta04_dai,
+ .num_links = 1,
+ .suspend_pre = gta04_audio_suspend,
+ .resume_post = gta04_audio_resume,
+};
+
+static struct platform_device *omap3gta04_snd_device;
+
+static int __init omap3gta04_soc_init(void)
+{
+ int ret;
+
+#if 0
+ if (!machine_is_gta04() && !machine_is_omap3_gta04()) {
+ pr_debug("Not GTA04!\n");
+ return -ENODEV;
+ }
+#endif
+ pr_info("GTA04 OMAP3 SoC snd init\n");
+
+ // FIXME: set any GPIOs i.e. enable Audio in/out switch
+ // microphone power etc.
+
+ omap3gta04_snd_device = platform_device_alloc("soc-audio", 0);
+ if (!omap3gta04_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(omap3gta04_snd_device, &snd_soc_omap3gta04);
+
+ ret = platform_device_add(omap3gta04_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(omap3gta04_snd_device);
+
+ return ret;
+}
+
+static void __exit omap3gta04_soc_exit(void)
+{
+ platform_device_unregister(omap3gta04_snd_device);
+}
+
+module_init(omap3gta04_soc_init);
+module_exit(omap3gta04_soc_exit);
+
+MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 GTA04");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/gta04-fm.c b/sound/soc/omap/gta04-fm.c
new file mode 100644
index 0000000..228a8b0
--- /dev/null
+++ b/sound/soc/omap/gta04-fm.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2011 John Ogness
+ * Author: John Ogness <john.ogness@linutronix.de>
+ *
+ * based on sound/soc/omap/omap3beagle.c by
+ * Steve Sakoman <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * 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/platform_device.h>
+#include <linux/module.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/si47xx.h"
+
+static int gta04_fm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ /* setup codec dai and cpu dai hardware 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->cpu_dai;
+ unsigned int fmt;
+ int ret;
+
+ fmt = SND_SOC_DAIFMT_I2S | // I2S
+ SND_SOC_DAIFMT_IB_IF | // positive sync pulse, driven on rising, sampled on falling clock
+ SND_SOC_DAIFMT_CBM_CFM; // clocks come from FM tuner
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKX_EXT, 0,
+ SND_SOC_CLOCK_IN);
+ // FIXME: set clock divisor
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gta04_fm_init(struct snd_soc_pcm_runtime *runtime)
+{
+ /* add controls */
+ /* add routes */
+ /* setup pins */
+ struct snd_soc_codec *codec = runtime->codec;
+
+ snd_soc_dapm_sync(&codec->dapm);
+ return 0;
+}
+
+static int gta04_fm_startup(struct snd_pcm_substream *substream)
+{
+ /* enable clock used by codec */
+ return 0;
+}
+
+static void gta04_fm_shutdown(struct snd_pcm_substream *substream)
+{
+ /* disable clock used by codec */
+}
+
+static struct snd_soc_ops gta04_fm_ops = {
+ .startup = gta04_fm_startup,
+ .hw_params = gta04_fm_hw_params,
+ .shutdown = gta04_fm_shutdown,
+};
+
+/* digital fm interface glue - connects codec <--> cpu */
+static struct snd_soc_dai_link gta04_fm_dai = {
+ .name = "Si47xx",
+ .stream_name = "Si47xx",
+ .cpu_dai_name = "omap-mcbsp.1",
+ .platform_name = "omap-pcm-audio",
+ .codec_dai_name = "Si47xx",
+ .init = gta04_fm_init,
+ .ops = &gta04_fm_ops,
+};
+
+/* fm machine driver */
+static struct snd_soc_card gta04_fm_card = {
+ .name = "gta04-fm",
+ .dai_link = &gta04_fm_dai,
+ .num_links = 1,
+};
+
+/* fm subsystem */
+static struct si47xx_setup_data gta04_fm_soc_data = {
+ .i2c_bus = 2,
+ .i2c_address = 0x11,
+};
+static struct snd_soc_device gta04_fm_devdata = {
+ .card = &gta04_fm_card,
+ .codec_dev = &soc_codec_dev_si47xx,
+ .codec_data = &gta04_fm_soc_data,
+};
+
+static struct platform_device *gta04_fm_snd_device;
+
+static int __init gta04_fm_soc_init(void)
+{
+ struct device *dev;
+ int ret;
+
+ pr_info("gta04-fm SoC init\n");
+
+ gta04_fm_snd_device = platform_device_alloc("soc-audio", 3);
+ if (!gta04_fm_snd_device) {
+ printk(KERN_ERR "platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ dev = &gta04_fm_snd_device->dev;
+
+ platform_set_drvdata(gta04_fm_snd_device, &gta04_fm_card);
+
+ ret = platform_device_add(gta04_fm_snd_device);
+ if (ret) {
+ printk(KERN_ERR "unable to add platform device\n");
+ platform_device_put(gta04_fm_snd_device);
+ }
+
+ return ret;
+}
+
+static void __exit gta04_fm_soc_exit(void)
+{
+ platform_device_unregister(gta04_fm_snd_device);
+}
+
+module_init(gta04_fm_soc_init);
+module_exit(gta04_fm_soc_exit);
+
+MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
+MODULE_DESCRIPTION("ALSA SoC GTA04 FM");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/omap/gta04-headset.c b/sound/soc/omap/gta04-headset.c
new file mode 100644
index 0000000..61162ab
--- /dev/null
+++ b/sound/soc/omap/gta04-headset.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 John Ogness
+ * Author: John Ogness <john.ogness@linutronix.de>
+ *
+ * based on sound/soc/omap/omap3beagle.c by
+ * Steve Sakoman <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * 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/platform_device.h>
+
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "omap-mcbsp.h"
+#include "../codecs/w2cbw003-bt.h"
+
+static int gta04_headset_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ /* setup codec dai and cpu dai hardware 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->cpu_dai;
+ unsigned int fmt;
+ int ret;
+
+ fmt = SND_SOC_DAIFMT_I2S | // I2S
+ SND_SOC_DAIFMT_IB_IF | // positive sync pulse, driven on rising, sampled on falling clock
+ SND_SOC_DAIFMT_CBM_CFM; // clocks come from bluetooth modem - but this can be configured in the Modem chip
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKX_EXT, 0,
+ SND_SOC_CLOCK_IN);
+ // FIXME: set clock divisor
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gta04_headset_init(struct snd_soc_pcm_runtime *runtime)
+{
+ /* add controls */
+ /* add routes */
+ /* setup pins */
+ struct snd_soc_codec *codec = runtime->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static int gta04_headset_startup(struct snd_pcm_substream *substream)
+{
+ /* enable clock used by codec */
+ return 0;
+}
+
+static void gta04_headset_shutdown(struct snd_pcm_substream *substream)
+{
+ /* disable clock used by codec */
+}
+
+static struct snd_soc_ops gta04_headset_ops = {
+ .startup = gta04_headset_startup,
+ .hw_params = gta04_headset_hw_params,
+ .shutdown = gta04_headset_shutdown,
+};
+
+/* digital headset interface glue - connects codec <--> cpu */
+static struct snd_soc_dai_link gta04_headset_dai = {
+ .name = "W2CBW003",
+ .stream_name = "W2CBW003",
+ .cpu_dai_name = "omap-mcbsp.3",
+ .platform_name = "omap-pcm-audio",
+ .codec_dai_name = "W2CBW003",
+ .codec_name = "w2cbw003_codec_audio",
+ .init = gta04_headset_init,
+ .ops = &gta04_headset_ops,
+};
+
+/* headset machine driver */
+static struct snd_soc_card gta04_headset_card = {
+ .name = "gta04-headset",
+ .dai_link = &gta04_headset_dai,
+ .num_links = 1,
+};
+
+static struct platform_device *gta04_headset_snd_device;
+
+static int __init gta04_headset_soc_init(void)
+{
+ struct device *dev;
+ int ret;
+
+ pr_info("gta04-headset SoC init\n");
+
+ gta04_headset_snd_device = platform_device_alloc("soc-audio", 2);
+ if (!gta04_headset_snd_device) {
+ printk(KERN_ERR "platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ dev = &gta04_headset_snd_device->dev;
+
+ platform_set_drvdata(gta04_headset_snd_device, &gta04_headset_card);
+
+ ret = platform_device_add(gta04_headset_snd_device);
+ if (ret) {
+ printk(KERN_ERR "unable to add platform device\n");
+ platform_device_put(gta04_headset_snd_device);
+ }
+
+ return ret;
+}
+
+static void __exit gta04_headset_soc_exit(void)
+{
+ platform_device_unregister(gta04_headset_snd_device);
+}
+
+module_init(gta04_headset_soc_init);
+module_exit(gta04_headset_soc_exit);
+
+MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
+MODULE_DESCRIPTION("ALSA SoC GTA04 Headset");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/omap/gta04-jack.c b/sound/soc/omap/gta04-jack.c
new file mode 100644
index 0000000..0d96582
--- /dev/null
+++ b/sound/soc/omap/gta04-jack.c
@@ -0,0 +1,230 @@
+
+/*
+ * Jack driver for GTA04.
+ * Copyright Neil Brown <neilb@suse.de> 2013
+ *
+ * The DC current through the headset microphone pins is
+ * converted to a voltage which is presented on TWL4030 madc 7.
+ * To be able to read a current, the Headset Mic Bias must
+ * be enabled.
+ *
+ * When the jack device is open, enable the Headset Mic Bias
+ * and poll mdac 7 every 500msec. Once we see an insertion,
+ * we increase the rate to ever 50msec until we see a removal.
+ *
+ * There are 4 possible states:
+ * - Nothing plugged in, open circuit - voltage is low
+ * - short circuit due to headphone with no mic (3-contact TRS)
+ * inserted. Voltage is high.
+ * - short circuit due to button on headset being pushed.
+ * Voltage is also high.
+ * - Microphone is in circuit. Voltage is even higher. I don't
+ * understand how it can be higher than than with a short
+ * circuit, but that is what I measure.
+ *
+ * To differentiate between the two short circuits we look at how
+ * we got there. A transition from open to short means a 3-contact
+ * TRS with no mic. A transition from mic to short means the button
+ * on the mic was pressed.
+ *
+ * As different devices report different actual voltages we need
+ * some calibration. As we cannot do this automatically we complete
+ * precision, we allow user-space to tell us the calibration.
+ * We assume that open-circuit is always below 100, and other
+ * readings are above that.
+ * The highest level we see for 3 consecutive readings is assumed
+ * to be the 'microphone' level, and short-circuit is 5% below that.
+ * If headphones with no mic are inserted this will be wrong, but not
+ * terribly wrong. As soon as a headset with a mic is inserted it
+ * will get corrected and stay corrected.
+ * In order to keep this correct across a reboot, user-space can
+ * read the current setting from
+ * /sys/modules/snd_soc_gta04/parameters/jack_level.
+ * and then write back the value after reboot. Once a value is
+ * written, auto-calibration is disabled.
+ * Writing the value '0' can re-enable auto-calibration.
+ */
+
+#include <linux/input.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <linux/suspend.h>
+#include <linux/i2c/twl4030-madc.h>
+#include <linux/module.h>
+
+static struct {
+ struct snd_soc_jack hs_jack;
+ struct delayed_work jack_work;
+ struct snd_soc_codec *codec;
+ int open;
+ /* When any jack is present, we:
+ * - poll more quickly to catch button presses
+ * - assume a 'short' is 'button press', not 'headset has
+ * no mic
+ * 'present' stores SND_JACK_HEADPHONE or SND_JACK_HEADSET
+ * indicating what we think is present.
+ */
+ int present;
+ /* Calibration reports a single number which roughly
+ * points to 'short'.
+ * Less than half this is 'open circuit'.
+ * More than this is 'microphone
+ */
+ long level;
+ int level_fixed;
+ long level_new;
+ int level_count;
+} jack;
+
+static void gta04_jack_work(struct work_struct *work)
+{
+ long val;
+ long delay = msecs_to_jiffies(500);
+ int jackbits;
+
+ /* choose delay *before* checking presence so we still get
+ * one long delay on first insertion to help with debounce.
+ */
+ if (jack.present)
+ delay = msecs_to_jiffies(50);
+
+ val = twl4030_get_madc_conversion(7);
+ if (val < 0)
+ goto out;
+ /* On my device:
+ * open circuit = around 20
+ * short circuit = around 800, or 325 on another device
+ * microphone = around 830-840 !!! 345 on other device.
+ */
+ if (!jack.level_fixed) {
+ if (jack.level * 21/20 + 2 < val) {
+ if (jack.level_count == 0 ||
+ val < jack.level_new*21/20)
+ jack.level_new = val*20/21;
+ if (jack.level_count >= 3) {
+ jack.level = jack.level_new;
+ jack.level_count = 0;
+ } else
+ jack.level_count += 1;
+ } else
+ jack.level_count = 0;
+ }
+ if (val < jack.level / 2) {
+ /* open circuit */
+ jackbits = 0;
+ jack.present = 0;
+ /* debounce */
+ delay = msecs_to_jiffies(500);
+ } else if (val < jack.level) {
+ /* short */
+ if (jack.present == 0) {
+ /* Inserted headset with no mic */
+ jack.present = SND_JACK_HEADPHONE;
+ jackbits = jack.present;
+ } else if (jack.present & SND_JACK_MICROPHONE) {
+ /* mic shorted -> button press */
+ jackbits = SND_JACK_BTN_0 | jack.present;
+ } else {
+ /* headphones still present */
+ jackbits = jack.present;
+ }
+ } else {
+ /* There is a microphone there */
+ jack.present = SND_JACK_HEADSET;
+ jackbits = jack.present;
+ }
+ snd_soc_jack_report(&jack.hs_jack, jackbits,
+ SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+out:
+ if (jack.open)
+ schedule_delayed_work(&jack.jack_work, delay);
+}
+
+static int gta04_jack_pm_notify(struct notifier_block *b, unsigned long v, void *d)
+{
+ if (!jack.codec || !jack.open)
+ return 0;
+ switch(v) {
+ case PM_SUSPEND_PREPARE:
+ /* Disable Headset Mic Bias while asleep */
+ snd_soc_dapm_disable_pin(&jack.codec->dapm, "Headset Mic Bias");
+ snd_soc_dapm_sync(&jack.codec->dapm);
+ break;
+
+ case PM_POST_SUSPEND:
+ snd_soc_dapm_force_enable_pin(&jack.codec->dapm, "Headset Mic Bias");
+ snd_soc_dapm_sync(&jack.codec->dapm);
+ break;
+ default: break;
+ }
+ return 0;
+}
+
+static struct notifier_block gta04_jack_pm_notify_block = {
+ .notifier_call = gta04_jack_pm_notify,
+};
+
+static int gta04_jack_open(struct input_dev *dev)
+{
+ snd_soc_dapm_force_enable_pin(&jack.codec->dapm, "Headset Mic Bias");
+ snd_soc_dapm_sync(&jack.codec->dapm);
+ jack.open = 1;
+ schedule_delayed_work(&jack.jack_work, msecs_to_jiffies(100));
+ return 0;
+}
+
+static void gta04_jack_close(struct input_dev *dev)
+{
+ jack.open = 0;
+ cancel_delayed_work_sync(&jack.jack_work);
+ snd_soc_dapm_disable_pin(&jack.codec->dapm, "Headset Mic Bias");
+ snd_soc_dapm_sync(&jack.codec->dapm);
+}
+
+int gta04_jack_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret;
+ ret = snd_soc_jack_new(codec, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &jack.hs_jack);
+ if (ret)
+ return ret;
+ register_pm_notifier(&gta04_jack_pm_notify_block);
+
+ INIT_DELAYED_WORK(&jack.jack_work, gta04_jack_work);
+ jack.codec = codec;
+ if (jack.level < 100)
+ jack.level = 100;
+ jack.hs_jack.jack->input_dev->open = gta04_jack_open;
+ jack.hs_jack.jack->input_dev->close = gta04_jack_close;
+
+ return snd_soc_dapm_sync(dapm);
+}
+
+void gta04_jack_remove(struct snd_soc_codec *codec)
+{
+ unregister_pm_notifier(&gta04_jack_pm_notify_block);
+ cancel_delayed_work(&jack.jack_work);
+}
+
+static int get_level(char *buffer, struct kernel_param *kp)
+{
+ return sprintf(buffer, "%ld", jack.level);
+}
+static int set_level(const char *val, struct kernel_param *kp)
+{
+ long num;
+ if (kstrtol(val, 10, &num) < 0)
+ return -EINVAL;
+ if (num == 0) {
+ jack.level = 100;
+ jack.level_fixed = 0;
+ } else {
+ jack.level = num;
+ jack.level_fixed = 1;
+ }
+ return 0;
+}
+module_param_call(jack_level, set_level, get_level, NULL, S_IRUSR|S_IWUSR);
diff --git a/sound/soc/omap/gta04-voice.c b/sound/soc/omap/gta04-voice.c
new file mode 100644
index 0000000..bb54381
--- /dev/null
+++ b/sound/soc/omap/gta04-voice.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2011 John Ogness
+ * Author: John Ogness <john.ogness@linutronix.de>
+ *
+ * based on sound/soc/omap/omap3beagle.c by
+ * Steve Sakoman <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * 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/platform_device.h>
+#include <linux/module.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "omap-mcbsp.h"
+#include "../codecs/gtm601.h"
+
+static int gta04_voice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ /* setup codec dai and cpu dai hardware 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->cpu_dai;
+ unsigned int fmt;
+ int ret;
+
+ fmt = SND_SOC_DAIFMT_I2S | // I2S
+ // SND_SOC_DAIFMT_GATED | // try to power down if not needed
+ SND_SOC_DAIFMT_IB_IF | // positive sync pulse, driven on rising, sampled on falling clock
+ SND_SOC_DAIFMT_CBM_CFM; // clocks come from GSM modem
+
+#if 0 // set clocks to be outputs
+
+ fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_IB_IF |
+ SND_SOC_DAIFMT_CBS_CFS;
+
+#endif
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gta04_voice_init(struct snd_soc_pcm_runtime *runtime)
+{
+ /* add controls */
+ /* add routes */
+ /* setup pins */
+ struct snd_soc_codec *codec = runtime->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ snd_soc_dapm_sync(dapm);
+ return 0;
+}
+
+static int gta04_voice_startup(struct snd_pcm_substream *substream)
+{
+ /* enable clock used by codec */
+ /* clock is provided by the GTM601 */
+ return 0;
+}
+
+static void gta04_voice_shutdown(struct snd_pcm_substream *substream)
+{
+ /* disable clock used by codec */
+ /* clock is provided by the GTM601 */
+}
+
+static struct snd_soc_ops gta04_voice_ops = {
+ .startup = gta04_voice_startup,
+ .hw_params = gta04_voice_hw_params,
+ .shutdown = gta04_voice_shutdown,
+};
+
+/* digital voice interface glue - connects codec <--> cpu */
+static struct snd_soc_dai_link gta04_voice_dai = {
+ .name = "GTM601",
+ .stream_name = "GTM601",
+ .cpu_dai_name = "omap-mcbsp.4",
+ .platform_name = "omap-pcm-audio",
+ .codec_dai_name = "GTM601",
+ .codec_name = "gtm601_codec_audio",
+ .init = gta04_voice_init,
+ .ops = &gta04_voice_ops,
+};
+
+/* voice machine driver */
+static struct snd_soc_card gta04_voice_card = {
+ .name = "gta04-voice",
+ .dai_link = &gta04_voice_dai,
+ .num_links = 1,
+};
+
+/* voice subsystem */
+/*static struct snd_soc_device gta04_voice_devdata = {
+ .card = &gta04_voice_card,
+ .codec_dev = &soc_codec_dev_gtm601,
+};*/
+
+static struct platform_device *gta04_voice_snd_device;
+
+static int __init gta04_voice_soc_init(void)
+{
+ struct device *dev;
+ int ret;
+
+ pr_info("gta04-voice SoC init\n");
+
+ gta04_voice_snd_device = platform_device_alloc("soc-audio", 1);
+ if (!gta04_voice_snd_device) {
+ printk(KERN_ERR "platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ dev = &gta04_voice_snd_device->dev;
+
+ platform_set_drvdata(gta04_voice_snd_device, &gta04_voice_card);
+
+ ret = platform_device_add(gta04_voice_snd_device);
+ if (ret) {
+ printk(KERN_ERR "unable to add platform device\n");
+ platform_device_put(gta04_voice_snd_device);
+ }
+
+ return ret;
+}
+
+static void __exit gta04_voice_soc_exit(void)
+{
+ platform_device_unregister(gta04_voice_snd_device);
+}
+
+module_init(gta04_voice_soc_init);
+module_exit(gta04_voice_soc_exit);
+
+MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
+MODULE_DESCRIPTION("ALSA SoC GTA04 Voice");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c
index 2a9324f..3824f15 100644
--- a/sound/soc/omap/omap-twl4030.c
+++ b/sound/soc/omap/omap-twl4030.c
@@ -47,6 +47,7 @@
struct omap_twl4030 {
int jack_detect; /* board can detect jack events */
struct snd_soc_jack hs_jack;
+ void (*jack_remove)(struct snd_soc_codec *codec);
};
static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
@@ -229,6 +230,10 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic");
twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In");
+ if (pdata->jack_init &&
+ pdata->jack_init(codec))
+ priv->jack_remove = pdata->jack_remove;
+
return ret;
}
@@ -357,6 +362,8 @@ static int omap_twl4030_remove(struct platform_device *pdev)
snd_soc_jack_free_gpios(&priv->hs_jack,
ARRAY_SIZE(hs_jack_gpios),
hs_jack_gpios);
+ if (priv->jack_remove)
+ priv->jack_remove(NULL);
snd_soc_unregister_card(card);
return 0;