aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2011-11-18 16:28:12 +1100
committerPaul Kocialkowski <contact@paulk.fr>2013-05-05 18:27:08 +0200
commit6b165cb5856e4c568147eda2086b41126db749fc (patch)
tree733931b1d1748dc9f5aa7c6cbf8e0575143a5480
parent01e3929c3610061b594f81a5ae48041188d50bc2 (diff)
downloadkernel_goldelico_gta04-6b165cb5856e4c568147eda2086b41126db749fc.zip
kernel_goldelico_gta04-6b165cb5856e4c568147eda2086b41126db749fc.tar.gz
kernel_goldelico_gta04-6b165cb5856e4c568147eda2086b41126db749fc.tar.bz2
Add GTA04 sound/codec files
-rw-r--r--sound/soc/codecs/Kconfig25
-rw-r--r--sound/soc/codecs/Makefile6
-rw-r--r--sound/soc/codecs/gtm601.c129
-rw-r--r--sound/soc/codecs/gtm601.h23
-rw-r--r--sound/soc/codecs/si47xx.c520
-rw-r--r--sound/soc/codecs/si47xx.h32
-rw-r--r--sound/soc/codecs/twl4030.h53
-rw-r--r--sound/soc/codecs/w2cbw003-bt.c132
-rw-r--r--sound/soc/codecs/w2cbw003-bt.h23
-rw-r--r--sound/soc/omap/Kconfig8
-rw-r--r--sound/soc/omap/Makefile2
-rw-r--r--sound/soc/omap/gta04-audio.c255
-rw-r--r--sound/soc/omap/gta04-fm.c157
-rw-r--r--sound/soc/omap/gta04-headset.c154
-rw-r--r--sound/soc/omap/gta04-voice.c181
-rw-r--r--sound/soc/omap/gta04.c254
16 files changed, 1954 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 98175a0..d3bbca2 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -96,6 +96,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
@@ -386,3 +389,25 @@ config SND_SOC_WM2000
config SND_SOC_WM9090
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 fd85584..11fdb0a 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -88,6 +88,9 @@ 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_AC97_CODEC) += snd-soc-ac97.o
@@ -179,3 +182,6 @@ 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..22c41a4
--- /dev/null
+++ b/sound/soc/codecs/gtm601.c
@@ -0,0 +1,129 @@
+/*
+ * 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,
+ },
+};
+EXPORT_SYMBOL_GPL(gtm601_dai);
+
+static int gtm601_soc_probe(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+
+ /* register pcms */
+ ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ if (ret < 0)
+ printk(KERN_ERR "gtm601: failed to create pcms\n");
+ return ret;
+}
+
+static int gtm601_soc_remove(struct snd_soc_codec *codec)
+{
+ if (codec == NULL)
+ return 0;
+ snd_soc_free_ac97_codec(codec);
+ return 0;
+}
+
+struct snd_soc_codec_driver soc_codec_dev_gtm601 = {
+ .probe = gtm601_soc_probe,
+ .remove = gtm601_soc_remove,
+// .reg_cache_size = ARRAY_SIZE(ad1980_reg),
+// .reg_word_size = sizeof(u16),
+// .reg_cache_step = 2,
+// .write = ac97_write,
+// .read = ac97_read,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_gtm601);
+
+
+static __devinit int gtm601_platform_probe(struct platform_device *pdev)
+{
+// gtm601_dai.dev = &pdev->dev;
+// return snd_soc_register_dai(&gtm601_dai);
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_gtm601, &gtm601_dai, 1);
+}
+
+static int __devexit gtm601_platform_remove(struct platform_device *pdev)
+{
+// snd_soc_unregister_dai(&gtm601_dai);
+ 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 = __devexit_p(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..1012a3c
--- /dev/null
+++ b/sound/soc/codecs/si47xx.c
@@ -0,0 +1,520 @@
+/*
+ * 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 snd_soc_codec_device soc_codec_dev_si47xx;
+
+/*
+ * 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_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->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->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->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 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,
+};
+EXPORT_SYMBOL_GPL(si47xx_dai);
+
+static int si47xx_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;
+
+ si47xx_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int si47xx_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ si47xx_set_bias_level(codec, codec->suspend_bias_level);
+
+ return 0;
+}
+
+/*
+ * 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->set_bias_level = si47xx_set_bias_level;
+ codec->dai = &si47xx_dai;
+ codec->num_dai = 1;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->reg_cache_size = ARRAY_SIZE(si47xx_reg_defaults);
+ codec->reg_cache = kmemdup(si47xx_reg_defaults,
+ sizeof(si47xx_reg_defaults),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ 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;
+}
+
+static struct snd_soc_device *si47xx_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * Si47xx 2 wire address is determined by GPIO5
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+
+static int si47xx_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = si47xx_socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ 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);
+ 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..33fbe70
--- /dev/null
+++ b/sound/soc/codecs/si47xx.h
@@ -0,0 +1,32 @@
+/*
+ * 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_dai si47xx_dai;
+extern struct snd_soc_codec_device soc_codec_dev_si47xx;
+
+#endif
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..5582309
--- /dev/null
+++ b/sound/soc/codecs/w2cbw003-bt.c
@@ -0,0 +1,132 @@
+/*
+ * 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,
+ },
+ .playback = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = W2CBW003_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+};
+EXPORT_SYMBOL_GPL(w2cbw003_dai);
+
+static int w2cbw003_soc_probe(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+
+ /* register pcms */
+ ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ if (ret < 0)
+ printk(KERN_ERR "w2cbw003: failed to create pcms\n");
+
+ return ret;
+}
+
+static int w2cbw003_soc_remove(struct snd_soc_codec *codec)
+{
+ if (codec == NULL)
+ return 0;
+ snd_soc_free_ac97_codec(codec);
+ return 0;
+}
+
+struct snd_soc_codec_driver soc_codec_dev_w2cbw003 = {
+ .probe = w2cbw003_soc_probe,
+ .remove = w2cbw003_soc_remove,
+// .write = ac97_write,
+// .read = ac97_read,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_w2cbw003);
+
+
+static __devinit int w2cbw003_platform_probe(struct platform_device *pdev)
+{
+// w2cbw003_dai.dev = &pdev->dev;
+// return snd_soc_register_dai(&w2cbw003_dai);
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_w2cbw003, &w2cbw003_dai, 1);
+}
+
+static int __devexit w2cbw003_platform_remove(struct platform_device *pdev)
+{
+// snd_soc_unregister_dai(&w2cbw003_dai);
+ 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 = __devexit_p(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 99054cf..a084c03 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -118,6 +118,14 @@ config SND_OMAP_SOC_OMAP3_BEAGLE
Say Y if you want to add support for SoC audio on the Beagleboard or
the clone Devkit8000.
+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.
+
config SND_OMAP_SOC_ZOOM2
tristate "SoC Audio support for Zoom2"
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 6c2c87e..56f4e5c 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -19,6 +19,7 @@ snd-soc-sdp3430-objs := sdp3430.o
snd-soc-sdp4430-objs := sdp4430.o
snd-soc-omap3pandora-objs := omap3pandora.o
snd-soc-omap3beagle-objs := omap3beagle.o
+snd-soc-gta04-objs := gta04-audio.o gta04-voice.o gta04-headset.o gta04-fm.o
snd-soc-zoom2-objs := zoom2.o
snd-soc-igep0020-objs := igep0020.o
@@ -34,5 +35,6 @@ obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
obj-$(CONFIG_SND_OMAP_SOC_SDP4430) += snd-soc-sdp4430.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
+obj-$(CONFIG_SND_OMAP_SOC_GTA04) += snd-soc-gta04.o
obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o
diff --git a/sound/soc/omap/gta04-audio.c b/sound/soc/omap/gta04-audio.c
new file mode 100644
index 0000000..3d55038
--- /dev/null
+++ b/sound/soc/omap/gta04-audio.c
@@ -0,0 +1,255 @@
+/*
+ * 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 <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/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 int omap3gta04_init(struct snd_soc_codec *codec)
+{
+ int ret;
+ 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");
+
+ 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-mcpdm-dai.0",
+ .codec_dai_name = "twl4030-hifi",
+ .ops = &omap3gta04_ops,
+ .init = &omap3gta04_init
+ }
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap3gta04 = {
+ .name = "gta04",
+// .platform = &omap_soc_platform,
+ .dai_link = &omap3gta04_dai[0],
+ .num_links = ARRAY_SIZE(omap3gta04_dai),
+};
+
+/* Audio subsystem */
+/*static struct snd_soc_driver omap3gta04_snd_devdata = {
+ .card = &snd_soc_omap3gta04,
+ .codec_dev = &soc_codec_dev_twl4030,
+};*/
+
+static struct platform_device *omap3gta04_snd_device;
+
+static int __init omap3gta04_soc_init(void)
+{
+ int ret;
+
+/* if (!machine_is_gta04() && !machine_is_omap3_gta04()) {
+ pr_debug("Not GTA04!\n");
+ return -ENODEV;
+ }*/
+ 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", -1);
+ if (!omap3gta04_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(omap3gta04_snd_device, &snd_soc_omap3gta04);
+// omap3gta04_snd_devdata.dev = &omap3gta04_snd_device->dev;
+// *(unsigned int *)omap3gta04_dai[0].cpu_dai->private_data = 1; /* McBSP2 = TPS65950 */
+ 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);
+ // switch off power
+}
+
+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..9a5d74e
--- /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 <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->dai->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_codec *codec)
+{
+ /* add controls */
+ /* add routes */
+ /* setup pins */
+
+ snd_soc_dapm_sync(codec);
+ 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 = &omap_mcbsp_dai[3],
+ .codec_dai = &si47xx_dai,
+ .init = gta04_fm_init,
+ .ops = &gta04_fm_ops,
+};
+
+/* fm machine driver */
+static struct snd_soc_card gta04_fm_card = {
+ .name = "gta04-fm",
+ .platform = &omap_soc_platform,
+ .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);
+// gta04_fm_devdata.dev = &gta04_fm_snd_device->dev;
+// *(unsigned int *)gta04_fm_dai.cpu_dai->private_data = 0; // McBSP1
+
+ 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..e200ea7
--- /dev/null
+++ b/sound/soc/omap/gta04-headset.c
@@ -0,0 +1,154 @@
+/*
+ * 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 <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/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_codec *codec)
+{
+ /* add controls */
+ /* add routes */
+ /* setup pins */
+ 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 = &omap_mcbsp_dai[2],
+ .cpu_dai_name = "omap-mcpdm-dai.2",
+// .codec_dai = &w2cbw003_dai,
+ .codec_dai_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",
+// .platform = &omap_soc_platform,
+ .dai_link = &gta04_headset_dai,
+ .num_links = 1,
+};
+
+/* headset subsystem */
+/*static struct snd_soc_device gta04_headset_devdata = {
+ .card = &gta04_headset_card,
+ .codec_dev = &soc_codec_dev_w2cbw003,
+};*/
+
+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);
+// gta04_headset_devdata.dev = &gta04_headset_snd_device->dev;
+// *(unsigned int *)gta04_headset_dai.cpu_dai->private_data = 2; // McBSP3
+
+ 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-voice.c b/sound/soc/omap/gta04-voice.c
new file mode 100644
index 0000000..4c475edf
--- /dev/null
+++ b/sound/soc/omap/gta04-voice.c
@@ -0,0 +1,181 @@
+/*
+ * 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 <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/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 0 // for testing we switch to output - don't use on a board with GTM601 installed!!!
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLK, 26000000,
+ SND_SOC_CLOCK_OUT);
+ // set divider...
+#else
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKX_EXT, 0,
+ SND_SOC_CLOCK_IN);
+ if(cpu_dai == 0) {
+ /*
+ * could be necessary if we connect to McBSP1 - but we are on McBSP4
+ * see http://mailman.alsa-project.org/pipermail/alsa-devel/2009-August/020771.html
+ */
+ if(ret >= 0)
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_CLKR_SRC_CLKX, 0,
+ SND_SOC_CLOCK_IN);
+ if(ret >= 0)
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0,
+ SND_SOC_CLOCK_IN);
+ }
+#endif
+ 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_codec *codec)
+{
+ /* add controls */
+ /* add routes */
+ /* setup pins */
+ 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 = &omap_mcbsp_dai[1],
+ .cpu_dai_name = "omap-mcpdm-dai.1",
+// .codec_dai = &gtm601_dai,
+ .codec_dai_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",
+// .platform = &omap_soc_platform,
+ .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);
+// gta04_voice_devdata.dev = &gta04_voice_snd_device->dev;
+// *(unsigned int *)gta04_voice_dai.cpu_dai->private_data = 3; // McBSP4
+
+ 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/gta04.c b/sound/soc/omap/gta04.c
new file mode 100644
index 0000000..5191f26
--- /dev/null
+++ b/sound/soc/omap/gta04.c
@@ -0,0 +1,254 @@
+/*
+ * 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 <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/twl4030.h"
+
+static int omap3beagle_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 int omap3gta04_init(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(codec, gta04_dapm_widgets,
+ ARRAY_SIZE(gta04_dapm_widgets));
+ if (ret < 0)
+ return ret;
+
+ snd_soc_dapm_add_routes(codec, 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(codec, "PREDRIVEL");
+ snd_soc_dapm_nc_pin(codec, "PREDRIVER");
+ // snd_soc_dapm_nc_pin(codec, "HSOL");
+ // snd_soc_dapm_nc_pin(codec, "HSOR");
+ snd_soc_dapm_nc_pin(codec, "CARKITMIC");
+ snd_soc_dapm_nc_pin(codec, "CARKITL");
+ snd_soc_dapm_nc_pin(codec, "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(codec, "DIGIMIC0");
+ snd_soc_dapm_nc_pin(codec, "DIGIMIC1");
+
+ return snd_soc_dapm_sync(codec);
+}
+
+static struct snd_soc_ops omap3beagle_ops = {
+ .hw_params = omap3beagle_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3beagle_dai[] = {
+ {
+ .name = "TWL4030",
+ .stream_name = "TWL4030",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
+ .ops = &omap3beagle_ops,
+ .init = &omap3gta04_init
+ }
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap3beagle = {
+ .name = "gta04",
+ .platform = &omap_soc_platform,
+ .dai_link = &omap3beagle_dai[0],
+ .num_links = ARRAY_SIZE(omap3beagle_dai),
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap3beagle_snd_devdata = {
+ .card = &snd_soc_omap3beagle,
+ .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap3beagle_snd_device;
+
+static int __init omap3beagle_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_gta04() && !machine_is_omap3_beagle()) {
+ pr_debug("Not GTA04!\n");
+ return -ENODEV;
+ }
+ pr_info("GTA04 OMAP3 SoC snd init\n");
+
+ // FIXME: set any GPIOs i.e. enable Audio in/out switch
+ // microphone power etc.
+
+ omap3beagle_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!omap3beagle_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(omap3beagle_snd_device, &omap3beagle_snd_devdata);
+ omap3beagle_snd_devdata.dev = &omap3beagle_snd_device->dev;
+// *(unsigned int *)omap3beagle_dai[0].cpu_dai->private_data = 1; /* McBSP2 = TPS65950 */
+ ret = platform_device_add(omap3beagle_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(omap3beagle_snd_device);
+
+ return ret;
+}
+
+static void __exit omap3beagle_soc_exit(void)
+{
+ platform_device_unregister(omap3beagle_snd_device);
+ // switch off power
+}
+
+module_init(omap3beagle_soc_init);
+module_exit(omap3beagle_soc_exit);
+
+MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 GTA04");
+MODULE_LICENSE("GPL");