Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 13b95dd..50f6e07 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -6,8 +6,7 @@
*
* 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.
+ * the Free Software Foundation; only version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 0e97248..34712cb 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -5,8 +5,7 @@
*
* 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.
+ * the Free Software Foundation; only version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 8224db5..8dbfdcc 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -57,6 +57,7 @@
source "sound/soc/sh/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
+source "sound/soc/msm/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 0af7016..fff81f2 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -20,3 +20,4 @@
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
+obj-$(CONFIG_SND_SOC) += msm/
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c
index 42f699c..f673516 100644
--- a/sound/soc/atmel/atmel-pcm.c
+++ b/sound/soc/atmel/atmel-pcm.c
@@ -18,8 +18,7 @@
*
* 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.
+ * the Free Software Foundation; only version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -509,4 +508,4 @@
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
MODULE_DESCRIPTION("Atmel PCM module");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 9e59f68..3c8ea9c 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -12,8 +12,7 @@
*
* 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.
+ * the Free Software Foundation; only version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -489,4 +488,4 @@
MODULE_AUTHOR("Cliff Cai");
MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index c42fb73..4d6c165 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -12,8 +12,7 @@
*
* 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.
+ * the Free Software Foundation; only version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -328,4 +327,4 @@
MODULE_AUTHOR("Cliff Cai");
MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
index c95cc03..3c96242 100644
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -12,8 +12,7 @@
*
* 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.
+ * the Free Software Foundation; only version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -353,4 +352,4 @@
MODULE_AUTHOR("Barry Song");
MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 922f59f..b923a25 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -96,6 +96,7 @@
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_TIMPANI if MARIMBA_CORE
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
@@ -249,6 +250,9 @@
config SND_SOC_UDA1380
tristate
+config SND_SOC_WCD9310
+ tristate
+
config SND_SOC_WL1273
tristate
@@ -387,3 +391,6 @@
config SND_SOC_WM9090
tristate
+
+config SND_SOC_MSM_STUB
+ tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index fd85584..78740ab 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -38,6 +38,7 @@
snd-soc-twl6040-objs := twl6040.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
+snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm8350-objs := wm8350.o
@@ -81,7 +82,8 @@
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
snd-soc-jz4740-codec-objs := jz4740.o
-
+snd-soc-timpani-objs := timpani.o
+snd-soc-msm-stub-objs := msm_stub.o
# Amp
snd-soc-lm4857-objs := lm4857.o
snd-soc-max9877-objs := max9877.o
@@ -130,6 +132,7 @@
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WCD9310) += snd-soc-wcd9310.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
@@ -172,6 +175,7 @@
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
+obj-$(CONFIG_SND_SOC_MSM_STUB) += snd-soc-msm-stub.o
# Amp
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
diff --git a/sound/soc/codecs/msm_stub.c b/sound/soc/codecs/msm_stub.c
new file mode 100644
index 0000000..21691df
--- /dev/null
+++ b/sound/soc/codecs/msm_stub.c
@@ -0,0 +1,79 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+/* A dummy driver useful only to advertise hardware parameters */
+static struct snd_soc_dai_driver msm_stub_dais[] = {
+ {
+ .name = "msm-stub-rx",
+ .playback = { /* Support maximum range */
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+ {
+ .name = "msm-stub-tx",
+ .capture = { /* Support maximum range */
+ .stream_name = "Record",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ }
+};
+
+static struct snd_soc_codec_driver soc_msm_stub = {};
+
+static int __devinit msm_stub_dev_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_msm_stub, msm_stub_dais, ARRAY_SIZE(msm_stub_dais));
+}
+
+static int __devexit msm_stub_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_stub_driver = {
+ .driver = {
+ .name = "msm-stub-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_stub_dev_probe,
+ .remove = __devexit_p(msm_stub_dev_remove),
+};
+
+static int __init msm_stub_init(void)
+{
+ return platform_driver_register(&msm_stub_driver);
+}
+module_init(msm_stub_init);
+
+static void __exit msm_stub_exit(void)
+{
+ platform_driver_unregister(&msm_stub_driver);
+}
+module_exit(msm_stub_exit);
+
+MODULE_DESCRIPTION("Generic MSM CODEC driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/timpani.c b/sound/soc/codecs/timpani.c
new file mode 100644
index 0000000..786b2d6
--- /dev/null
+++ b/sound/soc/codecs/timpani.c
@@ -0,0 +1,482 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/marimba.h>
+#include <linux/mfd/timpani-audio.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/soc-dapm.h>
+/* Debug purpose */
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <mach/mpp.h>
+/* End of debug purpose */
+
+#define ADIE_CODEC_MAX 2
+
+struct adie_codec_register {
+ u8 reg;
+ u8 mask;
+ u8 val;
+};
+
+static struct adie_codec_register dmic_on[] = {
+ {0x80, 0x05, 0x05},
+ {0x80, 0x05, 0x00},
+ {0x83, 0x0C, 0x00},
+ {0x8A, 0xF0, 0x30},
+ {0x86, 0xFF, 0xAC},
+ {0x87, 0xFF, 0xAC},
+ {0x8A, 0xF0, 0xF0},
+ {0x82, 0x1F, 0x1E},
+ {0x83, 0x0C, 0x0C},
+ {0x92, 0x3F, 0x21},
+ {0x94, 0x3F, 0x24},
+ {0xA3, 0x39, 0x01},
+ {0xA8, 0x0F, 0x00},
+ {0xAB, 0x3F, 0x00},
+ {0x86, 0xFF, 0x00},
+ {0x87, 0xFF, 0x00},
+ {0x8A, 0xF0, 0xC0},
+};
+
+static struct adie_codec_register dmic_off[] = {
+ {0x8A, 0xF0, 0xF0},
+ {0x83, 0x0C, 0x00},
+ {0x92, 0xFF, 0x00},
+ {0x94, 0xFF, 0x1B},
+};
+
+static struct adie_codec_register spk_on[] = {
+ {0x80, 0x02, 0x02},
+ {0x80, 0x02, 0x00},
+ {0x83, 0x03, 0x00},
+ {0x8A, 0x0F, 0x03},
+ {0xA3, 0x02, 0x02},
+ {0x84, 0xFF, 0x00},
+ {0x85, 0xFF, 0x00},
+ {0x8A, 0x0F, 0x0C},
+ {0x81, 0xFF, 0x0E},
+ {0x83, 0x03, 0x03},
+ {0x24, 0x6F, 0x6C},
+ {0xB7, 0x01, 0x01},
+ {0x31, 0x01, 0x01},
+ {0x32, 0xF8, 0x08},
+ {0x32, 0xF8, 0x48},
+ {0x32, 0xF8, 0xF8},
+ {0xE0, 0xFE, 0xAC},
+ {0xE1, 0xFE, 0xAC},
+ {0x3A, 0x24, 0x24},
+ {0xE0, 0xFE, 0x3C},
+ {0xE1, 0xFE, 0x3C},
+ {0xE0, 0xFE, 0x1C},
+ {0xE1, 0xFE, 0x1C},
+ {0xE0, 0xFE, 0x10},
+ {0xE1, 0xFE, 0x10},
+};
+
+static struct adie_codec_register spk_off[] = {
+ {0x8A, 0x0F, 0x0F},
+ {0xE0, 0xFE, 0x1C},
+ {0xE1, 0xFE, 0x1C},
+ {0xE0, 0xFE, 0x3C},
+ {0xE1, 0xFE, 0x3C},
+ {0xE0, 0xFC, 0xAC},
+ {0xE1, 0xFC, 0xAC},
+ {0x32, 0xF8, 0x00},
+ {0x31, 0x05, 0x00},
+ {0x3A, 0x24, 0x00},
+};
+
+static struct adie_codec_register spk_mute[] = {
+ {0x84, 0xFF, 0xAC},
+ {0x85, 0xFF, 0xAC},
+ {0x8A, 0x0F, 0x0C},
+};
+
+static struct adie_codec_register spk_unmute[] = {
+ {0x84, 0xFF, 0x00},
+ {0x85, 0xFF, 0x00},
+ {0x8A, 0x0F, 0x0C},
+};
+
+struct adie_codec_path {
+ int rate; /* sample rate of path */
+ u32 reg_owner;
+};
+
+struct timpani_drv_data { /* member undecided */
+ struct snd_soc_codec codec;
+ struct adie_codec_path path[ADIE_CODEC_MAX];
+ u32 ref_cnt;
+ struct marimba_codec_platform_data *codec_pdata;
+};
+
+static struct snd_soc_codec *timpani_codec;
+
+enum /* regaccess blk id */
+{
+ RA_BLOCK_RX1 = 0,
+ RA_BLOCK_RX2,
+ RA_BLOCK_TX1,
+ RA_BLOCK_TX2,
+ RA_BLOCK_LB,
+ RA_BLOCK_SHARED_RX_LB,
+ RA_BLOCK_SHARED_TX,
+ RA_BLOCK_TXFE1,
+ RA_BLOCK_TXFE2,
+ RA_BLOCK_PA_COMMON,
+ RA_BLOCK_PA_EAR,
+ RA_BLOCK_PA_HPH,
+ RA_BLOCK_PA_LINE,
+ RA_BLOCK_PA_AUX,
+ RA_BLOCK_ADC,
+ RA_BLOCK_DMIC,
+ RA_BLOCK_TX_I2S,
+ RA_BLOCK_DRV,
+ RA_BLOCK_TEST,
+ RA_BLOCK_RESERVED,
+ RA_BLOCK_NUM,
+};
+
+enum /* regaccess onwer ID */
+{
+ RA_OWNER_NONE = 0,
+ RA_OWNER_PATH_RX1,
+ RA_OWNER_PATH_RX2,
+ RA_OWNER_PATH_TX1,
+ RA_OWNER_PATH_TX2,
+ RA_OWNER_PATH_LB,
+ RA_OWNER_DRV,
+ RA_OWNER_NUM,
+};
+
+struct reg_acc_blk_cfg {
+ u8 valid_owners[RA_OWNER_NUM];
+};
+
+struct timpani_regaccess {
+ u8 reg_addr;
+ u8 blk_mask[RA_BLOCK_NUM];
+ u8 reg_mask;
+ u8 reg_default;
+};
+
+static unsigned int timpani_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct marimba *pdrv = codec->control_data;
+ int rc;
+ u8 val;
+
+ rc = marimba_read(pdrv, reg, &val, 1);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to write reg %x\n", __func__, reg);
+ return 0;
+ }
+ return val;
+}
+
+static int timpani_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct marimba *pdrv = codec->control_data;
+ int rc;
+
+ rc = marimba_write_bit_mask(pdrv, reg, (u8 *)&value, 1, 0xFF);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: fail to write reg %x\n", __func__, reg);
+ return -EIO;
+ }
+ pr_debug("%s: write reg %x val %x\n", __func__, reg, value);
+ return 0;
+}
+
+static void timpani_codec_bring_up(struct snd_soc_codec *codec)
+{
+ struct timpani_drv_data *timpani = snd_soc_codec_get_drvdata(codec);
+ int rc;
+
+ if (timpani->codec_pdata &&
+ timpani->codec_pdata->marimba_codec_power) {
+ if (timpani->ref_cnt)
+ return;
+ /* Codec power up sequence */
+ rc = timpani->codec_pdata->marimba_codec_power(1);
+ if (rc)
+ pr_err("%s: could not power up timpani "
+ "codec\n", __func__);
+ else {
+ timpani_codec_write(codec, 0xFF, 0x08);
+ timpani_codec_write(codec, 0xFF, 0x0A);
+ timpani_codec_write(codec, 0xFF, 0x0E);
+ timpani_codec_write(codec, 0xFF, 0x07);
+ timpani_codec_write(codec, 0xFF, 0x17);
+ timpani_codec_write(codec, TIMPANI_A_MREF, 0x22);
+ msleep(15);
+ timpani->ref_cnt++;
+ }
+ }
+}
+
+static void timpani_codec_bring_down(struct snd_soc_codec *codec)
+{
+ struct timpani_drv_data *timpani = snd_soc_codec_get_drvdata(codec);
+ int rc;
+
+ if (timpani->codec_pdata &&
+ timpani->codec_pdata->marimba_codec_power) {
+ timpani->ref_cnt--;
+ if (timpani->ref_cnt >= 1)
+ return;
+ timpani_codec_write(codec, TIMPANI_A_MREF, TIMPANI_MREF_POR);
+ timpani_codec_write(codec, 0xFF, 0x07);
+ timpani_codec_write(codec, 0xFF, 0x06);
+ timpani_codec_write(codec, 0xFF, 0x0E);
+ timpani_codec_write(codec, 0xFF, 0x08);
+ rc = timpani->codec_pdata->marimba_codec_power(0);
+ if (rc)
+ pr_err("%s: could not power down timpani "
+ "codec\n", __func__);
+ }
+}
+
+static void timpani_dmic_config(struct snd_soc_codec *codec, int on)
+{
+ struct adie_codec_register *regs;
+ int regs_sz, i;
+
+ if (on) {
+ regs = dmic_on;
+ regs_sz = ARRAY_SIZE(dmic_on);
+ } else {
+ regs = dmic_off;
+ regs_sz = ARRAY_SIZE(dmic_off);
+ }
+
+ for (i = 0; i < regs_sz; i++)
+ timpani_codec_write(codec, regs[i].reg,
+ (regs[i].mask & regs[i].val));
+}
+
+static void timpani_spk_config(struct snd_soc_codec *codec, int on)
+{
+ struct adie_codec_register *regs;
+ int regs_sz, i;
+
+ if (on) {
+ regs = spk_on;
+ regs_sz = ARRAY_SIZE(spk_on);
+ } else {
+ regs = spk_off;
+ regs_sz = ARRAY_SIZE(spk_off);
+ }
+
+ for (i = 0; i < regs_sz; i++)
+ timpani_codec_write(codec, regs[i].reg,
+ (regs[i].mask & regs[i].val));
+}
+
+static int timpani_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ pr_info("%s()\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ pr_info("%s: playback\n", __func__);
+ timpani_codec_bring_up(codec);
+ timpani_spk_config(codec, 1);
+ } else {
+ pr_info("%s: Capture\n", __func__);
+ timpani_codec_bring_up(codec);
+ timpani_dmic_config(codec, 1);
+ }
+ return 0;
+}
+
+static void timpani_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ pr_info("%s()\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ timpani_codec_bring_down(codec);
+ timpani_spk_config(codec, 0);
+ } else {
+ timpani_codec_bring_down(codec);
+ timpani_dmic_config(codec, 0);
+ }
+ return;
+}
+
+int digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adie_codec_register *regs;
+ int regs_sz, i;
+
+ if (mute) {
+ regs = spk_mute;
+ regs_sz = ARRAY_SIZE(spk_mute);
+ } else {
+ regs = spk_unmute;
+ regs_sz = ARRAY_SIZE(spk_unmute);
+ }
+
+ for (i = 0; i < regs_sz; i++) {
+ timpani_codec_write(codec, regs[i].reg,
+ (regs[i].mask & regs[i].val));
+ msleep(10);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops timpani_dai_ops = {
+ .startup = timpani_startup,
+ .shutdown = timpani_shutdown,
+};
+
+struct snd_soc_dai timpani_codec_dai[] = {
+ {
+ .name = "TIMPANI Rx",
+ .playback = {
+ .stream_name = "Handset Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_max = 96000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &timpani_dai_ops,
+ },
+ {
+ .name = "TIMPANI Tx",
+ .capture = {
+ .stream_name = "Handset Capture",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rate_max = 96000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &timpani_dai_ops,
+ }
+};
+
+static int timpani_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (!timpani_codec) {
+ dev_err(&pdev->dev, "core driver not yet probed\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = timpani_codec;
+ codec = timpani_codec;
+
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0)
+ dev_err(codec->dev, "failed to create pcms\n");
+ return ret;
+}
+
+/* power down chip */
+static int timpani_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_timpani = {
+ .probe = timpani_soc_probe,
+ .remove = timpani_soc_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_timpani);
+
+static int timpani_codec_probe(struct platform_device *pdev)
+{
+ struct snd_soc_codec *codec;
+ struct timpani_drv_data *priv;
+
+ pr_info("%s()\n", __func__);
+ priv = kzalloc(sizeof(struct timpani_drv_data), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ codec = &priv->codec;
+ snd_soc_codec_set_drvdata(codec, priv);
+ priv->codec_pdata = pdev->dev.platform_data;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->name = "TIMPANI";
+ codec->owner = THIS_MODULE;
+ codec->read = timpani_codec_read;
+ codec->write = timpani_codec_write;
+ codec->dai = timpani_codec_dai;
+ codec->num_dai = ARRAY_SIZE(timpani_codec_dai);
+ codec->control_data = platform_get_drvdata(pdev);
+ timpani_codec = codec;
+
+ snd_soc_register_dais(timpani_codec_dai, ARRAY_SIZE(timpani_codec_dai));
+ snd_soc_register_codec(codec);
+
+ return 0;
+}
+
+static struct platform_driver timpani_codec_driver = {
+ .probe = timpani_codec_probe,
+ .driver = {
+ .name = "timpani_codec",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init timpani_codec_init(void)
+{
+ return platform_driver_register(&timpani_codec_driver);
+}
+
+static void __exit timpani_codec_exit(void)
+{
+ platform_driver_unregister(&timpani_codec_driver);
+}
+
+module_init(timpani_codec_init);
+module_exit(timpani_codec_exit);
+
+MODULE_DESCRIPTION("Timpani codec driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/timpani.h b/sound/soc/codecs/timpani.h
new file mode 100644
index 0000000..bd14eea
--- /dev/null
+++ b/sound/soc/codecs/timpani.h
@@ -0,0 +1,15 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+#define NUM_I2S 2
+extern struct snd_soc_dai timpani_codec_dai[NUM_I2S];
+extern struct snd_soc_codec_device soc_codec_dev_timpani;
diff --git a/sound/soc/codecs/wcd9310-tables.c b/sound/soc/codecs/wcd9310-tables.c
new file mode 100644
index 0000000..bc6bf4f
--- /dev/null
+++ b/sound/soc/codecs/wcd9310-tables.c
@@ -0,0 +1,1024 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/mfd/wcd9310/registers.h>
+#include "wcd9310.h"
+
+const u8 tabla_reg_readable[TABLA_CACHE_SIZE] = {
+ [TABLA_A_CHIP_CTL] = 1,
+ [TABLA_A_CHIP_STATUS] = 1,
+ [TABLA_A_CHIP_ID_BYTE_0] = 1,
+ [TABLA_A_CHIP_ID_BYTE_1] = 1,
+ [TABLA_A_CHIP_ID_BYTE_2] = 1,
+ [TABLA_A_CHIP_ID_BYTE_3] = 1,
+ [TABLA_A_CHIP_VERSION] = 1,
+ [TABLA_A_SB_VERSION] = 1,
+ [TABLA_A_SLAVE_ID_1] = 1,
+ [TABLA_A_SLAVE_ID_2] = 1,
+ [TABLA_A_SLAVE_ID_3] = 1,
+ [TABLA_A_PIN_CTL_OE0] = 1,
+ [TABLA_A_PIN_CTL_OE1] = 1,
+ [TABLA_A_PIN_CTL_DATA0] = 1,
+ [TABLA_A_PIN_CTL_DATA1] = 1,
+ [TABLA_A_HDRIVE_GENERIC] = 1,
+ [TABLA_A_HDRIVE_OVERRIDE] = 1,
+ [TABLA_A_ANA_CSR_WAIT_STATE] = 1,
+ [TABLA_A_PROCESS_MONITOR_CTL0] = 1,
+ [TABLA_A_PROCESS_MONITOR_CTL1] = 1,
+ [TABLA_A_PROCESS_MONITOR_CTL2] = 1,
+ [TABLA_A_PROCESS_MONITOR_CTL3] = 1,
+ [TABLA_A_QFUSE_CTL] = 1,
+ [TABLA_A_QFUSE_STATUS] = 1,
+ [TABLA_A_QFUSE_DATA_OUT0] = 1,
+ [TABLA_A_QFUSE_DATA_OUT1] = 1,
+ [TABLA_A_QFUSE_DATA_OUT2] = 1,
+ [TABLA_A_QFUSE_DATA_OUT3] = 1,
+ [TABLA_A_CDC_CTL] = 1,
+ [TABLA_A_LEAKAGE_CTL] = 1,
+ [TABLA_A_INTR_MODE] = 1,
+ [TABLA_A_INTR_MASK0] = 1,
+ [TABLA_A_INTR_MASK1] = 1,
+ [TABLA_A_INTR_MASK2] = 1,
+ [TABLA_A_INTR_STATUS0] = 1,
+ [TABLA_A_INTR_STATUS1] = 1,
+ [TABLA_A_INTR_STATUS2] = 1,
+ [TABLA_A_INTR_CLEAR0] = 0,
+ [TABLA_A_INTR_CLEAR1] = 0,
+ [TABLA_A_INTR_CLEAR2] = 0,
+ [TABLA_A_INTR_LEVEL0] = 1,
+ [TABLA_A_INTR_LEVEL1] = 1,
+ [TABLA_A_INTR_LEVEL2] = 1,
+ [TABLA_A_INTR_TEST0] = 1,
+ [TABLA_A_INTR_TEST1] = 1,
+ [TABLA_A_INTR_TEST2] = 1,
+ [TABLA_A_INTR_SET0] = 1,
+ [TABLA_A_INTR_SET1] = 1,
+ [TABLA_A_INTR_SET2] = 1,
+ [TABLA_A_CDC_TX_I2S_SCK_MODE] = 1,
+ [TABLA_A_CDC_TX_I2S_WS_MODE] = 1,
+ [TABLA_A_CDC_DMIC_DATA0_MODE] = 1,
+ [TABLA_A_CDC_DMIC_CLK0_MODE] = 1,
+ [TABLA_A_CDC_DMIC_DATA1_MODE] = 1,
+ [TABLA_A_CDC_DMIC_CLK1_MODE] = 1,
+ [TABLA_A_CDC_RX_I2S_SCK_MODE] = 1,
+ [TABLA_A_CDC_RX_I2S_WS_MODE] = 1,
+ [TABLA_A_CDC_DMIC_DATA2_MODE] = 1,
+ [TABLA_A_CDC_DMIC_CLK2_MODE] = 1,
+ [TABLA_A_CDC_INTR_MODE] = 1,
+ [TABLA_A_BIAS_REF_CTL] = 1,
+ [TABLA_A_BIAS_CENTRAL_BG_CTL] = 1,
+ [TABLA_A_BIAS_PRECHRG_CTL] = 1,
+ [TABLA_A_BIAS_CURR_CTL_1] = 1,
+ [TABLA_A_BIAS_CURR_CTL_2] = 1,
+ [TABLA_A_BIAS_CONFIG_MODE_BG_CTL] = 1,
+ [TABLA_A_BIAS_BG_STATUS] = 1,
+ [TABLA_A_CLK_BUFF_EN1] = 1,
+ [TABLA_A_CLK_BUFF_EN2] = 1,
+ [TABLA_A_LDO_H_MODE_1] = 1,
+ [TABLA_A_LDO_H_MODE_2] = 1,
+ [TABLA_A_LDO_H_LOOP_CTL] = 1,
+ [TABLA_A_LDO_H_COMP_1] = 1,
+ [TABLA_A_LDO_H_COMP_2] = 1,
+ [TABLA_A_LDO_H_BIAS_1] = 1,
+ [TABLA_A_LDO_H_BIAS_2] = 1,
+ [TABLA_A_LDO_H_BIAS_3] = 1,
+ [TABLA_A_LDO_L_MODE_1] = 1,
+ [TABLA_A_LDO_L_MODE_2] = 1,
+ [TABLA_A_LDO_L_LOOP_CTL] = 1,
+ [TABLA_A_LDO_L_COMP_1] = 1,
+ [TABLA_A_LDO_L_COMP_2] = 1,
+ [TABLA_A_LDO_L_BIAS_1] = 1,
+ [TABLA_A_LDO_L_BIAS_2] = 1,
+ [TABLA_A_LDO_L_BIAS_3] = 1,
+ [TABLA_A_MICB_CFILT_1_CTL] = 1,
+ [TABLA_A_MICB_CFILT_1_VAL] = 1,
+ [TABLA_A_MICB_CFILT_1_PRECHRG] = 1,
+ [TABLA_A_MICB_1_CTL] = 1,
+ [TABLA_A_MICB_1_INT_RBIAS] = 1,
+ [TABLA_A_MICB_1_MBHC] = 1,
+ [TABLA_A_MICB_CFILT_2_CTL] = 1,
+ [TABLA_A_MICB_CFILT_2_VAL] = 1,
+ [TABLA_A_MICB_CFILT_2_PRECHRG] = 1,
+ [TABLA_A_MICB_2_CTL] = 1,
+ [TABLA_A_MICB_2_INT_RBIAS] = 1,
+ [TABLA_A_MICB_2_MBHC] = 1,
+ [TABLA_A_MICB_CFILT_3_CTL] = 1,
+ [TABLA_A_MICB_CFILT_3_VAL] = 1,
+ [TABLA_A_MICB_CFILT_3_PRECHRG] = 1,
+ [TABLA_A_MICB_3_CTL] = 1,
+ [TABLA_A_MICB_3_INT_RBIAS] = 1,
+ [TABLA_A_MICB_3_MBHC] = 1,
+ [TABLA_A_MICB_4_CTL] = 1,
+ [TABLA_A_MICB_4_INT_RBIAS] = 1,
+ [TABLA_A_MICB_4_MBHC] = 1,
+ [TABLA_A_TX_COM_BIAS] = 1,
+ [TABLA_A_MBHC_SCALING_MUX_1] = 1,
+ [TABLA_A_MBHC_SCALING_MUX_2] = 1,
+ [TABLA_A_TX_SUP_SWITCH_CTRL_1] = 1,
+ [TABLA_A_TX_SUP_SWITCH_CTRL_2] = 1,
+ [TABLA_A_TX_1_2_EN] = 1,
+ [TABLA_A_TX_1_2_TEST_EN] = 1,
+ [TABLA_A_TX_1_2_ADC_CH1] = 1,
+ [TABLA_A_TX_1_2_ADC_CH2] = 1,
+ [TABLA_A_TX_1_2_ATEST_REFCTRL] = 1,
+ [TABLA_A_TX_1_2_TEST_CTL] = 1,
+ [TABLA_A_TX_1_2_TEST_BLOCK_EN] = 1,
+ [TABLA_A_TX_1_2_TXFE_CLKDIV] = 1,
+ [TABLA_A_TX_1_2_SAR_ERR_CH1] = 1,
+ [TABLA_A_TX_1_2_SAR_ERR_CH2] = 1,
+ [TABLA_A_TX_3_4_EN] = 1,
+ [TABLA_A_TX_3_4_TEST_EN] = 1,
+ [TABLA_A_TX_3_4_ADC_CH3] = 1,
+ [TABLA_A_TX_3_4_ADC_CH4] = 1,
+ [TABLA_A_TX_3_4_ATEST_REFCTRL] = 1,
+ [TABLA_A_TX_3_4_TEST_CTL] = 1,
+ [TABLA_A_TX_3_4_TEST_BLOCK_EN] = 1,
+ [TABLA_A_TX_3_4_TXFE_CKDIV] = 1,
+ [TABLA_A_TX_3_4_SAR_ERR_CH3] = 1,
+ [TABLA_A_TX_3_4_SAR_ERR_CH4] = 1,
+ [TABLA_A_TX_5_6_EN] = 1,
+ [TABLA_A_TX_5_6_TEST_EN] = 1,
+ [TABLA_A_TX_5_6_ADC_CH5] = 1,
+ [TABLA_A_TX_5_6_ADC_CH6] = 1,
+ [TABLA_A_TX_5_6_ATEST_REFCTRL] = 1,
+ [TABLA_A_TX_5_6_TEST_CTL] = 1,
+ [TABLA_A_TX_5_6_TEST_BLOCK_EN] = 1,
+ [TABLA_A_TX_5_6_TXFE_CKDIV] = 1,
+ [TABLA_A_TX_5_6_SAR_ERR_CH5] = 1,
+ [TABLA_A_TX_5_6_SAR_ERR_CH6] = 1,
+ [TABLA_A_TX_7_MBHC_EN] = 1,
+ [TABLA_A_TX_7_MBHC_ATEST_REFCTRL] = 1,
+ [TABLA_A_TX_7_MBHC_ADC] = 1,
+ [TABLA_A_TX_7_MBHC_TEST_CTL] = 1,
+ [TABLA_A_TX_7_MBHC_SAR_ERR] = 1,
+ [TABLA_A_TX_7_TXFE_CLKDIV] = 1,
+ [TABLA_A_AUX_COM_CTL] = 1,
+ [TABLA_A_AUX_COM_ATEST] = 1,
+ [TABLA_A_AUX_L_EN] = 1,
+ [TABLA_A_AUX_L_GAIN] = 1,
+ [TABLA_A_AUX_L_PA_CONN] = 1,
+ [TABLA_A_AUX_L_PA_CONN_INV] = 1,
+ [TABLA_A_AUX_R_EN] = 1,
+ [TABLA_A_AUX_R_GAIN] = 1,
+ [TABLA_A_AUX_R_PA_CONN] = 1,
+ [TABLA_A_AUX_R_PA_CONN_INV] = 1,
+ [TABLA_A_CP_EN] = 1,
+ [TABLA_A_CP_CLK] = 1,
+ [TABLA_A_CP_STATIC] = 1,
+ [TABLA_A_CP_DCC1] = 1,
+ [TABLA_A_CP_DCC3] = 1,
+ [TABLA_A_CP_ATEST] = 1,
+ [TABLA_A_CP_DTEST] = 1,
+ [TABLA_A_RX_COM_TIMER_DIV] = 1,
+ [TABLA_A_RX_COM_OCP_CTL] = 1,
+ [TABLA_A_RX_COM_OCP_COUNT] = 1,
+ [TABLA_A_RX_COM_DAC_CTL] = 1,
+ [TABLA_A_RX_COM_BIAS] = 1,
+ [TABLA_A_RX_HPH_BIAS_PA] = 1,
+ [TABLA_A_RX_HPH_BIAS_LDO] = 1,
+ [TABLA_A_RX_HPH_BIAS_CNP] = 1,
+ [TABLA_A_RX_HPH_BIAS_WG] = 1,
+ [TABLA_A_RX_HPH_OCP_CTL] = 1,
+ [TABLA_A_RX_HPH_CNP_EN] = 1,
+ [TABLA_A_RX_HPH_CNP_WG_CTL] = 1,
+ [TABLA_A_RX_HPH_CNP_WG_TIME] = 1,
+ [TABLA_A_RX_HPH_L_GAIN] = 1,
+ [TABLA_A_RX_HPH_L_TEST] = 1,
+ [TABLA_A_RX_HPH_L_PA_CTL] = 1,
+ [TABLA_A_RX_HPH_L_DAC_CTL] = 1,
+ [TABLA_A_RX_HPH_L_ATEST] = 1,
+ [TABLA_A_RX_HPH_L_STATUS] = 1,
+ [TABLA_A_RX_HPH_R_GAIN] = 1,
+ [TABLA_A_RX_HPH_R_TEST] = 1,
+ [TABLA_A_RX_HPH_R_PA_CTL] = 1,
+ [TABLA_A_RX_HPH_R_DAC_CTL] = 1,
+ [TABLA_A_RX_HPH_R_ATEST] = 1,
+ [TABLA_A_RX_HPH_R_STATUS] = 1,
+ [TABLA_A_RX_EAR_BIAS_PA] = 1,
+ [TABLA_A_RX_EAR_BIAS_CMBUFF] = 1,
+ [TABLA_A_RX_EAR_EN] = 1,
+ [TABLA_A_RX_EAR_GAIN] = 1,
+ [TABLA_A_RX_EAR_CMBUFF] = 1,
+ [TABLA_A_RX_EAR_ICTL] = 1,
+ [TABLA_A_RX_EAR_CCOMP] = 1,
+ [TABLA_A_RX_EAR_VCM] = 1,
+ [TABLA_A_RX_EAR_CNP] = 1,
+ [TABLA_A_RX_EAR_ATEST] = 1,
+ [TABLA_A_RX_EAR_STATUS] = 1,
+ [TABLA_A_RX_LINE_BIAS_PA] = 1,
+ [TABLA_A_RX_LINE_BIAS_DAC] = 1,
+ [TABLA_A_RX_LINE_BIAS_CNP] = 1,
+ [TABLA_A_RX_LINE_COM] = 1,
+ [TABLA_A_RX_LINE_CNP_EN] = 1,
+ [TABLA_A_RX_LINE_CNP_WG_CTL] = 1,
+ [TABLA_A_RX_LINE_CNP_WG_TIME] = 1,
+ [TABLA_A_RX_LINE_1_GAIN] = 1,
+ [TABLA_A_RX_LINE_1_TEST] = 1,
+ [TABLA_A_RX_LINE_1_DAC_CTL] = 1,
+ [TABLA_A_RX_LINE_1_STATUS] = 1,
+ [TABLA_A_RX_LINE_2_GAIN] = 1,
+ [TABLA_A_RX_LINE_2_TEST] = 1,
+ [TABLA_A_RX_LINE_2_DAC_CTL] = 1,
+ [TABLA_A_RX_LINE_2_STATUS] = 1,
+ [TABLA_A_RX_LINE_3_GAIN] = 1,
+ [TABLA_A_RX_LINE_3_TEST] = 1,
+ [TABLA_A_RX_LINE_3_DAC_CTL] = 1,
+ [TABLA_A_RX_LINE_3_STATUS] = 1,
+ [TABLA_A_RX_LINE_4_GAIN] = 1,
+ [TABLA_A_RX_LINE_4_TEST] = 1,
+ [TABLA_A_RX_LINE_4_DAC_CTL] = 1,
+ [TABLA_A_RX_LINE_4_STATUS] = 1,
+ [TABLA_A_RX_LINE_5_GAIN] = 1,
+ [TABLA_A_RX_LINE_5_TEST] = 1,
+ [TABLA_A_RX_LINE_5_DAC_CTL] = 1,
+ [TABLA_A_RX_LINE_5_STATUS] = 1,
+ [TABLA_A_RX_LINE_CNP_DBG] = 1,
+ [TABLA_A_MBHC_HPH] = 1,
+ [TABLA_A_CONFIG_MODE_FREQ] = 1,
+ [TABLA_A_CONFIG_MODE_TEST] = 1,
+ [TABLA_A_CONFIG_MODE_STATUS] = 1,
+ [TABLA_A_CONFIG_MODE_TUNER] = 1,
+ [TABLA_A_CDC_TX1_VOL_CTL_TIMER] = 1,
+ [TABLA_A_CDC_TX2_VOL_CTL_TIMER] = 1,
+ [TABLA_A_CDC_TX3_VOL_CTL_TIMER] = 1,
+ [TABLA_A_CDC_TX4_VOL_CTL_TIMER] = 1,
+ [TABLA_A_CDC_TX5_VOL_CTL_TIMER] = 1,
+ [TABLA_A_CDC_TX6_VOL_CTL_TIMER] = 1,
+ [TABLA_A_CDC_TX7_VOL_CTL_TIMER] = 1,
+ [TABLA_A_CDC_TX8_VOL_CTL_TIMER] = 1,
+ [TABLA_A_CDC_TX9_VOL_CTL_TIMER] = 1,
+ [TABLA_A_CDC_TX10_VOL_CTL_TIMER] = 1,
+ [TABLA_A_CDC_TX1_VOL_CTL_GAIN] = 1,
+ [TABLA_A_CDC_TX2_VOL_CTL_GAIN] = 1,
+ [TABLA_A_CDC_TX3_VOL_CTL_GAIN] = 1,
+ [TABLA_A_CDC_TX4_VOL_CTL_GAIN] = 1,
+ [TABLA_A_CDC_TX5_VOL_CTL_GAIN] = 1,
+ [TABLA_A_CDC_TX6_VOL_CTL_GAIN] = 1,
+ [TABLA_A_CDC_TX7_VOL_CTL_GAIN] = 1,
+ [TABLA_A_CDC_TX8_VOL_CTL_GAIN] = 1,
+ [TABLA_A_CDC_TX9_VOL_CTL_GAIN] = 1,
+ [TABLA_A_CDC_TX10_VOL_CTL_GAIN] = 1,
+ [TABLA_A_CDC_TX1_VOL_CTL_CFG] = 1,
+ [TABLA_A_CDC_TX2_VOL_CTL_CFG] = 1,
+ [TABLA_A_CDC_TX3_VOL_CTL_CFG] = 1,
+ [TABLA_A_CDC_TX4_VOL_CTL_CFG] = 1,
+ [TABLA_A_CDC_TX5_VOL_CTL_CFG] = 1,
+ [TABLA_A_CDC_TX6_VOL_CTL_CFG] = 1,
+ [TABLA_A_CDC_TX7_VOL_CTL_CFG] = 1,
+ [TABLA_A_CDC_TX8_VOL_CTL_CFG] = 1,
+ [TABLA_A_CDC_TX9_VOL_CTL_CFG] = 1,
+ [TABLA_A_CDC_TX10_VOL_CTL_CFG] = 1,
+ [TABLA_A_CDC_TX1_MUX_CTL] = 1,
+ [TABLA_A_CDC_TX2_MUX_CTL] = 1,
+ [TABLA_A_CDC_TX3_MUX_CTL] = 1,
+ [TABLA_A_CDC_TX4_MUX_CTL] = 1,
+ [TABLA_A_CDC_TX5_MUX_CTL] = 1,
+ [TABLA_A_CDC_TX6_MUX_CTL] = 1,
+ [TABLA_A_CDC_TX7_MUX_CTL] = 1,
+ [TABLA_A_CDC_TX8_MUX_CTL] = 1,
+ [TABLA_A_CDC_TX9_MUX_CTL] = 1,
+ [TABLA_A_CDC_TX10_MUX_CTL] = 1,
+ [TABLA_A_CDC_TX1_CLK_FS_CTL] = 1,
+ [TABLA_A_CDC_TX2_CLK_FS_CTL] = 1,
+ [TABLA_A_CDC_TX3_CLK_FS_CTL] = 1,
+ [TABLA_A_CDC_TX4_CLK_FS_CTL] = 1,
+ [TABLA_A_CDC_TX5_CLK_FS_CTL] = 1,
+ [TABLA_A_CDC_TX6_CLK_FS_CTL] = 1,
+ [TABLA_A_CDC_TX7_CLK_FS_CTL] = 1,
+ [TABLA_A_CDC_TX8_CLK_FS_CTL] = 1,
+ [TABLA_A_CDC_TX9_CLK_FS_CTL] = 1,
+ [TABLA_A_CDC_TX10_CLK_FS_CTL] = 1,
+ [TABLA_A_CDC_TX1_DMIC_CTL] = 1,
+ [TABLA_A_CDC_TX2_DMIC_CTL] = 1,
+ [TABLA_A_CDC_TX3_DMIC_CTL] = 1,
+ [TABLA_A_CDC_TX4_DMIC_CTL] = 1,
+ [TABLA_A_CDC_TX5_DMIC_CTL] = 1,
+ [TABLA_A_CDC_TX6_DMIC_CTL] = 1,
+ [TABLA_A_CDC_TX7_DMIC_CTL] = 1,
+ [TABLA_A_CDC_TX8_DMIC_CTL] = 1,
+ [TABLA_A_CDC_TX9_DMIC_CTL] = 1,
+ [TABLA_A_CDC_TX10_DMIC_CTL] = 1,
+ [TABLA_A_CDC_SRC1_PDA_CFG] = 1,
+ [TABLA_A_CDC_SRC2_PDA_CFG] = 1,
+ [TABLA_A_CDC_SRC1_FS_CTL] = 1,
+ [TABLA_A_CDC_SRC2_FS_CTL] = 1,
+ [TABLA_A_CDC_RX1_B1_CTL] = 1,
+ [TABLA_A_CDC_RX2_B1_CTL] = 1,
+ [TABLA_A_CDC_RX3_B1_CTL] = 1,
+ [TABLA_A_CDC_RX4_B1_CTL] = 1,
+ [TABLA_A_CDC_RX5_B1_CTL] = 1,
+ [TABLA_A_CDC_RX6_B1_CTL] = 1,
+ [TABLA_A_CDC_RX7_B1_CTL] = 1,
+ [TABLA_A_CDC_RX1_B2_CTL] = 1,
+ [TABLA_A_CDC_RX2_B2_CTL] = 1,
+ [TABLA_A_CDC_RX3_B2_CTL] = 1,
+ [TABLA_A_CDC_RX4_B2_CTL] = 1,
+ [TABLA_A_CDC_RX5_B2_CTL] = 1,
+ [TABLA_A_CDC_RX6_B2_CTL] = 1,
+ [TABLA_A_CDC_RX7_B2_CTL] = 1,
+ [TABLA_A_CDC_RX1_B3_CTL] = 1,
+ [TABLA_A_CDC_RX2_B3_CTL] = 1,
+ [TABLA_A_CDC_RX3_B3_CTL] = 1,
+ [TABLA_A_CDC_RX4_B3_CTL] = 1,
+ [TABLA_A_CDC_RX5_B3_CTL] = 1,
+ [TABLA_A_CDC_RX6_B3_CTL] = 1,
+ [TABLA_A_CDC_RX7_B3_CTL] = 1,
+ [TABLA_A_CDC_RX1_B4_CTL] = 1,
+ [TABLA_A_CDC_RX2_B4_CTL] = 1,
+ [TABLA_A_CDC_RX3_B4_CTL] = 1,
+ [TABLA_A_CDC_RX4_B4_CTL] = 1,
+ [TABLA_A_CDC_RX5_B4_CTL] = 1,
+ [TABLA_A_CDC_RX6_B4_CTL] = 1,
+ [TABLA_A_CDC_RX7_B4_CTL] = 1,
+ [TABLA_A_CDC_RX1_B5_CTL] = 1,
+ [TABLA_A_CDC_RX2_B5_CTL] = 1,
+ [TABLA_A_CDC_RX3_B5_CTL] = 1,
+ [TABLA_A_CDC_RX4_B5_CTL] = 1,
+ [TABLA_A_CDC_RX5_B5_CTL] = 1,
+ [TABLA_A_CDC_RX6_B5_CTL] = 1,
+ [TABLA_A_CDC_RX7_B5_CTL] = 1,
+ [TABLA_A_CDC_RX1_B6_CTL] = 1,
+ [TABLA_A_CDC_RX2_B6_CTL] = 1,
+ [TABLA_A_CDC_RX3_B6_CTL] = 1,
+ [TABLA_A_CDC_RX4_B6_CTL] = 1,
+ [TABLA_A_CDC_RX5_B6_CTL] = 1,
+ [TABLA_A_CDC_RX6_B6_CTL] = 1,
+ [TABLA_A_CDC_RX7_B6_CTL] = 1,
+ [TABLA_A_CDC_RX1_VOL_CTL_B1_CTL] = 1,
+ [TABLA_A_CDC_RX2_VOL_CTL_B1_CTL] = 1,
+ [TABLA_A_CDC_RX3_VOL_CTL_B1_CTL] = 1,
+ [TABLA_A_CDC_RX4_VOL_CTL_B1_CTL] = 1,
+ [TABLA_A_CDC_RX5_VOL_CTL_B1_CTL] = 1,
+ [TABLA_A_CDC_RX6_VOL_CTL_B1_CTL] = 1,
+ [TABLA_A_CDC_RX7_VOL_CTL_B1_CTL] = 1,
+ [TABLA_A_CDC_RX1_VOL_CTL_B2_CTL] = 1,
+ [TABLA_A_CDC_RX2_VOL_CTL_B2_CTL] = 1,
+ [TABLA_A_CDC_RX3_VOL_CTL_B2_CTL] = 1,
+ [TABLA_A_CDC_RX4_VOL_CTL_B2_CTL] = 1,
+ [TABLA_A_CDC_RX5_VOL_CTL_B2_CTL] = 1,
+ [TABLA_A_CDC_RX6_VOL_CTL_B2_CTL] = 1,
+ [TABLA_A_CDC_RX7_VOL_CTL_B2_CTL] = 1,
+ [TABLA_A_CDC_CLK_RX_RESET_CTL] = 1,
+ [TABLA_A_CDC_CLK_TX_RESET_B1_CTL] = 1,
+ [TABLA_A_CDC_CLK_TX_RESET_B2_CTL] = 1,
+ [TABLA_A_CDC_CLK_DMIC_CTL] = 1,
+ [TABLA_A_CDC_CLK_RX_I2S_CTL] = 1,
+ [TABLA_A_CDC_CLK_TX_I2S_CTL] = 1,
+ [TABLA_A_CDC_CLK_OTHR_RESET_CTL] = 1,
+ [TABLA_A_CDC_CLK_TX_CLK_EN_B1_CTL] = 1,
+ [TABLA_A_CDC_CLK_TX_CLK_EN_B2_CTL] = 1,
+ [TABLA_A_CDC_CLK_OTHR_CTL] = 1,
+ [TABLA_A_CDC_CLK_RDAC_CLK_EN_CTL] = 1,
+ [TABLA_A_CDC_CLK_RX_B1_CTL] = 1,
+ [TABLA_A_CDC_CLK_RX_B2_CTL] = 1,
+ [TABLA_A_CDC_CLK_MCLK_CTL] = 1,
+ [TABLA_A_CDC_CLK_PDM_CTL] = 1,
+ [TABLA_A_CDC_CLK_SD_CTL] = 1,
+ [TABLA_A_CDC_CLSG_FREQ_THRESH_B1_CTL] = 1,
+ [TABLA_A_CDC_CLSG_FREQ_THRESH_B2_CTL] = 1,
+ [TABLA_A_CDC_CLSG_FREQ_THRESH_B3_CTL] = 1,
+ [TABLA_A_CDC_CLSG_FREQ_THRESH_B4_CTL] = 1,
+ [TABLA_A_CDC_CLSG_GAIN_THRESH_CTL] = 1,
+ [TABLA_A_CDC_CLSG_TIMER_B1_CFG] = 1,
+ [TABLA_A_CDC_CLSG_TIMER_B2_CFG] = 1,
+ [TABLA_A_CDC_CLSG_CTL] = 1,
+ [TABLA_A_CDC_IIR1_GAIN_B1_CTL] = 1,
+ [TABLA_A_CDC_IIR2_GAIN_B1_CTL] = 1,
+ [TABLA_A_CDC_IIR1_GAIN_B2_CTL] = 1,
+ [TABLA_A_CDC_IIR2_GAIN_B2_CTL] = 1,
+ [TABLA_A_CDC_IIR1_GAIN_B3_CTL] = 1,
+ [TABLA_A_CDC_IIR2_GAIN_B3_CTL] = 1,
+ [TABLA_A_CDC_IIR1_GAIN_B4_CTL] = 1,
+ [TABLA_A_CDC_IIR2_GAIN_B4_CTL] = 1,
+ [TABLA_A_CDC_IIR1_GAIN_B5_CTL] = 1,
+ [TABLA_A_CDC_IIR2_GAIN_B5_CTL] = 1,
+ [TABLA_A_CDC_IIR1_GAIN_B6_CTL] = 1,
+ [TABLA_A_CDC_IIR2_GAIN_B6_CTL] = 1,
+ [TABLA_A_CDC_IIR1_GAIN_B7_CTL] = 1,
+ [TABLA_A_CDC_IIR2_GAIN_B7_CTL] = 1,
+ [TABLA_A_CDC_IIR1_GAIN_B8_CTL] = 1,
+ [TABLA_A_CDC_IIR2_GAIN_B8_CTL] = 1,
+ [TABLA_A_CDC_IIR1_CTL] = 1,
+ [TABLA_A_CDC_IIR2_CTL] = 1,
+ [TABLA_A_CDC_IIR1_GAIN_TIMER_CTL] = 1,
+ [TABLA_A_CDC_IIR2_GAIN_TIMER_CTL] = 1,
+ [TABLA_A_CDC_IIR1_COEF_B1_CTL] = 1,
+ [TABLA_A_CDC_IIR2_COEF_B1_CTL] = 1,
+ [TABLA_A_CDC_IIR1_COEF_B2_CTL] = 1,
+ [TABLA_A_CDC_IIR2_COEF_B2_CTL] = 1,
+ [TABLA_A_CDC_IIR1_COEF_B3_CTL] = 1,
+ [TABLA_A_CDC_IIR2_COEF_B3_CTL] = 1,
+ [TABLA_A_CDC_IIR1_COEF_B4_CTL] = 1,
+ [TABLA_A_CDC_IIR2_COEF_B4_CTL] = 1,
+ [TABLA_A_CDC_IIR1_COEF_B5_CTL] = 1,
+ [TABLA_A_CDC_IIR2_COEF_B5_CTL] = 1,
+ [TABLA_A_CDC_TOP_GAIN_UPDATE] = 1,
+ [TABLA_A_CDC_DEBUG_B1_CTL] = 1,
+ [TABLA_A_CDC_DEBUG_B2_CTL] = 1,
+ [TABLA_A_CDC_DEBUG_B3_CTL] = 1,
+ [TABLA_A_CDC_DEBUG_B4_CTL] = 1,
+ [TABLA_A_CDC_DEBUG_B5_CTL] = 1,
+ [TABLA_A_CDC_DEBUG_B6_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX1_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX1_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX1_B3_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX2_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX2_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX2_B3_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX3_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX3_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX3_B3_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX4_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX4_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX5_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX5_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX6_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX6_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX7_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX7_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_B3_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_B4_CTL] = 1,
+ [TABLA_A_CDC_CONN_EQ1_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_EQ1_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_EQ1_B3_CTL] = 1,
+ [TABLA_A_CDC_CONN_EQ1_B4_CTL] = 1,
+ [TABLA_A_CDC_CONN_EQ2_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_EQ2_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_EQ2_B3_CTL] = 1,
+ [TABLA_A_CDC_CONN_EQ2_B4_CTL] = 1,
+ [TABLA_A_CDC_CONN_SRC1_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_SRC1_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_SRC2_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_SRC2_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B3_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B4_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B5_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B6_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B7_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B8_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B9_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B10_CTL] = 1,
+ [TABLA_A_CDC_CONN_TX_SB_B11_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX_SB_B1_CTL] = 1,
+ [TABLA_A_CDC_CONN_RX_SB_B2_CTL] = 1,
+ [TABLA_A_CDC_CONN_CLSG_CTL] = 1,
+ [TABLA_A_CDC_CONN_SPARE] = 1,
+ [TABLA_A_CDC_MBHC_EN_CTL] = 1,
+ [TABLA_A_CDC_MBHC_FEATURE_B1_CFG] = 1,
+ [TABLA_A_CDC_MBHC_FEATURE_B2_CFG] = 1,
+ [TABLA_A_CDC_MBHC_TIMER_B1_CTL] = 1,
+ [TABLA_A_CDC_MBHC_TIMER_B2_CTL] = 1,
+ [TABLA_A_CDC_MBHC_TIMER_B3_CTL] = 1,
+ [TABLA_A_CDC_MBHC_TIMER_B4_CTL] = 1,
+ [TABLA_A_CDC_MBHC_TIMER_B5_CTL] = 1,
+ [TABLA_A_CDC_MBHC_TIMER_B6_CTL] = 1,
+ [TABLA_A_CDC_MBHC_B1_STATUS] = 1,
+ [TABLA_A_CDC_MBHC_B2_STATUS] = 1,
+ [TABLA_A_CDC_MBHC_B3_STATUS] = 1,
+ [TABLA_A_CDC_MBHC_B4_STATUS] = 1,
+ [TABLA_A_CDC_MBHC_B5_STATUS] = 1,
+ [TABLA_A_CDC_MBHC_B1_CTL] = 1,
+ [TABLA_A_CDC_MBHC_B2_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B1_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B2_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B3_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B4_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B5_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B6_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B7_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B8_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B9_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B10_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B11_CTL] = 1,
+ [TABLA_A_CDC_MBHC_VOLT_B12_CTL] = 1,
+ [TABLA_A_CDC_MBHC_CLK_CTL] = 1,
+ [TABLA_A_CDC_MBHC_INT_CTL] = 1,
+ [TABLA_A_CDC_MBHC_DEBUG_CTL] = 1,
+ [TABLA_A_CDC_MBHC_SPARE] = 1,
+};
+
+const u8 tabla_reg_defaults[TABLA_CACHE_SIZE] = {
+ [TABLA_A_CHIP_CTL] = TABLA_A_CHIP_CTL__POR,
+ [TABLA_A_CHIP_STATUS] = TABLA_A_CHIP_STATUS__POR,
+ [TABLA_A_CHIP_ID_BYTE_0] = TABLA_A_CHIP_ID_BYTE_0__POR,
+ [TABLA_A_CHIP_ID_BYTE_1] = TABLA_A_CHIP_ID_BYTE_1__POR,
+ [TABLA_A_CHIP_ID_BYTE_2] = TABLA_A_CHIP_ID_BYTE_2__POR,
+ [TABLA_A_CHIP_ID_BYTE_3] = TABLA_A_CHIP_ID_BYTE_3__POR,
+ [TABLA_A_CHIP_VERSION] = TABLA_A_CHIP_VERSION__POR,
+ [TABLA_A_SB_VERSION] = TABLA_A_SB_VERSION__POR,
+ [TABLA_A_SLAVE_ID_1] = TABLA_A_SLAVE_ID_1__POR,
+ [TABLA_A_SLAVE_ID_2] = TABLA_A_SLAVE_ID_2__POR,
+ [TABLA_A_SLAVE_ID_3] = TABLA_A_SLAVE_ID_3__POR,
+ [TABLA_A_PIN_CTL_OE0] = TABLA_A_PIN_CTL_OE0__POR,
+ [TABLA_A_PIN_CTL_OE1] = TABLA_A_PIN_CTL_OE1__POR,
+ [TABLA_A_PIN_CTL_DATA0] = TABLA_A_PIN_CTL_DATA0__POR,
+ [TABLA_A_PIN_CTL_DATA1] = TABLA_A_PIN_CTL_DATA1__POR,
+ [TABLA_A_HDRIVE_GENERIC] = TABLA_A_HDRIVE_GENERIC__POR,
+ [TABLA_A_HDRIVE_OVERRIDE] = TABLA_A_HDRIVE_OVERRIDE__POR,
+ [TABLA_A_ANA_CSR_WAIT_STATE] = TABLA_A_ANA_CSR_WAIT_STATE__POR,
+ [TABLA_A_PROCESS_MONITOR_CTL0] = TABLA_A_PROCESS_MONITOR_CTL0__POR,
+ [TABLA_A_PROCESS_MONITOR_CTL1] = TABLA_A_PROCESS_MONITOR_CTL1__POR,
+ [TABLA_A_PROCESS_MONITOR_CTL2] = TABLA_A_PROCESS_MONITOR_CTL2__POR,
+ [TABLA_A_PROCESS_MONITOR_CTL3] = TABLA_A_PROCESS_MONITOR_CTL3__POR,
+ [TABLA_A_QFUSE_CTL] = TABLA_A_QFUSE_CTL__POR,
+ [TABLA_A_QFUSE_STATUS] = TABLA_A_QFUSE_STATUS__POR,
+ [TABLA_A_QFUSE_DATA_OUT0] = TABLA_A_QFUSE_DATA_OUT0__POR,
+ [TABLA_A_QFUSE_DATA_OUT1] = TABLA_A_QFUSE_DATA_OUT1__POR,
+ [TABLA_A_QFUSE_DATA_OUT2] = TABLA_A_QFUSE_DATA_OUT2__POR,
+ [TABLA_A_QFUSE_DATA_OUT3] = TABLA_A_QFUSE_DATA_OUT3__POR,
+ [TABLA_A_CDC_CTL] = TABLA_A_CDC_CTL__POR,
+ [TABLA_A_LEAKAGE_CTL] = TABLA_A_LEAKAGE_CTL__POR,
+ [TABLA_A_INTR_MODE] = TABLA_A_INTR_MODE__POR,
+ [TABLA_A_INTR_MASK0] = TABLA_A_INTR_MASK0__POR,
+ [TABLA_A_INTR_MASK1] = TABLA_A_INTR_MASK1__POR,
+ [TABLA_A_INTR_MASK2] = TABLA_A_INTR_MASK2__POR,
+ [TABLA_A_INTR_STATUS0] = TABLA_A_INTR_STATUS0__POR,
+ [TABLA_A_INTR_STATUS1] = TABLA_A_INTR_STATUS1__POR,
+ [TABLA_A_INTR_STATUS2] = TABLA_A_INTR_STATUS2__POR,
+ [TABLA_A_INTR_CLEAR0] = TABLA_A_INTR_CLEAR0__POR,
+ [TABLA_A_INTR_CLEAR1] = TABLA_A_INTR_CLEAR1__POR,
+ [TABLA_A_INTR_CLEAR2] = TABLA_A_INTR_CLEAR2__POR,
+ [TABLA_A_INTR_LEVEL0] = TABLA_A_INTR_LEVEL0__POR,
+ [TABLA_A_INTR_LEVEL1] = TABLA_A_INTR_LEVEL1__POR,
+ [TABLA_A_INTR_LEVEL2] = TABLA_A_INTR_LEVEL2__POR,
+ [TABLA_A_INTR_TEST0] = TABLA_A_INTR_TEST0__POR,
+ [TABLA_A_INTR_TEST1] = TABLA_A_INTR_TEST1__POR,
+ [TABLA_A_INTR_TEST2] = TABLA_A_INTR_TEST2__POR,
+ [TABLA_A_INTR_SET0] = TABLA_A_INTR_SET0__POR,
+ [TABLA_A_INTR_SET1] = TABLA_A_INTR_SET1__POR,
+ [TABLA_A_INTR_SET2] = TABLA_A_INTR_SET2__POR,
+ [TABLA_A_CDC_TX_I2S_SCK_MODE] = TABLA_A_CDC_TX_I2S_SCK_MODE__POR,
+ [TABLA_A_CDC_TX_I2S_WS_MODE] = TABLA_A_CDC_TX_I2S_WS_MODE__POR,
+ [TABLA_A_CDC_DMIC_DATA0_MODE] = TABLA_A_CDC_DMIC_DATA0_MODE__POR,
+ [TABLA_A_CDC_DMIC_CLK0_MODE] = TABLA_A_CDC_DMIC_CLK0_MODE__POR,
+ [TABLA_A_CDC_DMIC_DATA1_MODE] = TABLA_A_CDC_DMIC_DATA1_MODE__POR,
+ [TABLA_A_CDC_DMIC_CLK1_MODE] = TABLA_A_CDC_DMIC_CLK1_MODE__POR,
+ [TABLA_A_CDC_RX_I2S_SCK_MODE] = TABLA_A_CDC_RX_I2S_SCK_MODE__POR,
+ [TABLA_A_CDC_RX_I2S_WS_MODE] = TABLA_A_CDC_RX_I2S_WS_MODE__POR,
+ [TABLA_A_CDC_DMIC_DATA2_MODE] = TABLA_A_CDC_DMIC_DATA2_MODE__POR,
+ [TABLA_A_CDC_DMIC_CLK2_MODE] = TABLA_A_CDC_DMIC_CLK2_MODE__POR,
+ [TABLA_A_CDC_INTR_MODE] = TABLA_A_CDC_INTR_MODE__POR,
+ [TABLA_A_BIAS_REF_CTL] = TABLA_A_BIAS_REF_CTL__POR,
+ [TABLA_A_BIAS_CENTRAL_BG_CTL] = TABLA_A_BIAS_CENTRAL_BG_CTL__POR,
+ [TABLA_A_BIAS_PRECHRG_CTL] = TABLA_A_BIAS_PRECHRG_CTL__POR,
+ [TABLA_A_BIAS_CURR_CTL_1] = TABLA_A_BIAS_CURR_CTL_1__POR,
+ [TABLA_A_BIAS_CURR_CTL_2] = TABLA_A_BIAS_CURR_CTL_2__POR,
+ [TABLA_A_BIAS_CONFIG_MODE_BG_CTL] =
+ TABLA_A_BIAS_CONFIG_MODE_BG_CTL__POR,
+ [TABLA_A_BIAS_BG_STATUS] = TABLA_A_BIAS_BG_STATUS__POR,
+ [TABLA_A_CLK_BUFF_EN1] = TABLA_A_CLK_BUFF_EN1__POR,
+ [TABLA_A_CLK_BUFF_EN2] = TABLA_A_CLK_BUFF_EN2__POR,
+ [TABLA_A_LDO_H_MODE_1] = TABLA_A_LDO_H_MODE_1__POR,
+ [TABLA_A_LDO_H_MODE_2] = TABLA_A_LDO_H_MODE_2__POR,
+ [TABLA_A_LDO_H_LOOP_CTL] = TABLA_A_LDO_H_LOOP_CTL__POR,
+ [TABLA_A_LDO_H_COMP_1] = TABLA_A_LDO_H_COMP_1__POR,
+ [TABLA_A_LDO_H_COMP_2] = TABLA_A_LDO_H_COMP_2__POR,
+ [TABLA_A_LDO_H_BIAS_1] = TABLA_A_LDO_H_BIAS_1__POR,
+ [TABLA_A_LDO_H_BIAS_2] = TABLA_A_LDO_H_BIAS_2__POR,
+ [TABLA_A_LDO_H_BIAS_3] = TABLA_A_LDO_H_BIAS_3__POR,
+ [TABLA_A_LDO_L_MODE_1] = TABLA_A_LDO_L_MODE_1__POR,
+ [TABLA_A_LDO_L_MODE_2] = TABLA_A_LDO_L_MODE_2__POR,
+ [TABLA_A_LDO_L_LOOP_CTL] = TABLA_A_LDO_L_LOOP_CTL__POR,
+ [TABLA_A_LDO_L_COMP_1] = TABLA_A_LDO_L_COMP_1__POR,
+ [TABLA_A_LDO_L_COMP_2] = TABLA_A_LDO_L_COMP_2__POR,
+ [TABLA_A_LDO_L_BIAS_1] = TABLA_A_LDO_L_BIAS_1__POR,
+ [TABLA_A_LDO_L_BIAS_2] = TABLA_A_LDO_L_BIAS_2__POR,
+ [TABLA_A_LDO_L_BIAS_3] = TABLA_A_LDO_L_BIAS_3__POR,
+ [TABLA_A_MICB_CFILT_1_CTL] = TABLA_A_MICB_CFILT_1_CTL__POR,
+ [TABLA_A_MICB_CFILT_1_VAL] = TABLA_A_MICB_CFILT_1_VAL__POR,
+ [TABLA_A_MICB_CFILT_1_PRECHRG] = TABLA_A_MICB_CFILT_1_PRECHRG__POR,
+ [TABLA_A_MICB_1_CTL] = TABLA_A_MICB_1_CTL__POR,
+ [TABLA_A_MICB_1_INT_RBIAS] = TABLA_A_MICB_1_INT_RBIAS__POR,
+ [TABLA_A_MICB_1_MBHC] = TABLA_A_MICB_1_MBHC__POR,
+ [TABLA_A_MICB_CFILT_2_CTL] = TABLA_A_MICB_CFILT_2_CTL__POR,
+ [TABLA_A_MICB_CFILT_2_VAL] = TABLA_A_MICB_CFILT_2_VAL__POR,
+ [TABLA_A_MICB_CFILT_2_PRECHRG] = TABLA_A_MICB_CFILT_2_PRECHRG__POR,
+ [TABLA_A_MICB_2_CTL] = TABLA_A_MICB_2_CTL__POR,
+ [TABLA_A_MICB_2_INT_RBIAS] = TABLA_A_MICB_2_INT_RBIAS__POR,
+ [TABLA_A_MICB_2_MBHC] = TABLA_A_MICB_2_MBHC__POR,
+ [TABLA_A_MICB_CFILT_3_CTL] = TABLA_A_MICB_CFILT_3_CTL__POR,
+ [TABLA_A_MICB_CFILT_3_VAL] = TABLA_A_MICB_CFILT_3_VAL__POR,
+ [TABLA_A_MICB_CFILT_3_PRECHRG] = TABLA_A_MICB_CFILT_3_PRECHRG__POR,
+ [TABLA_A_MICB_3_CTL] = TABLA_A_MICB_3_CTL__POR,
+ [TABLA_A_MICB_3_INT_RBIAS] = TABLA_A_MICB_3_INT_RBIAS__POR,
+ [TABLA_A_MICB_3_MBHC] = TABLA_A_MICB_3_MBHC__POR,
+ [TABLA_A_MICB_4_CTL] = TABLA_A_MICB_4_CTL__POR,
+ [TABLA_A_MICB_4_INT_RBIAS] = TABLA_A_MICB_4_INT_RBIAS__POR,
+ [TABLA_A_MICB_4_MBHC] = TABLA_A_MICB_4_MBHC__POR,
+ [TABLA_A_TX_COM_BIAS] = TABLA_A_TX_COM_BIAS__POR,
+ [TABLA_A_MBHC_SCALING_MUX_1] = TABLA_A_MBHC_SCALING_MUX_1__POR,
+ [TABLA_A_MBHC_SCALING_MUX_2] = TABLA_A_MBHC_SCALING_MUX_2__POR,
+ [TABLA_A_TX_SUP_SWITCH_CTRL_1] = TABLA_A_TX_SUP_SWITCH_CTRL_1__POR,
+ [TABLA_A_TX_SUP_SWITCH_CTRL_2] = TABLA_A_TX_SUP_SWITCH_CTRL_2__POR,
+ [TABLA_A_TX_1_2_EN] = TABLA_A_TX_1_2_EN__POR,
+ [TABLA_A_TX_1_2_TEST_EN] = TABLA_A_TX_1_2_TEST_EN__POR,
+ [TABLA_A_TX_1_2_ADC_CH1] = TABLA_A_TX_1_2_ADC_CH1__POR,
+ [TABLA_A_TX_1_2_ADC_CH2] = TABLA_A_TX_1_2_ADC_CH2__POR,
+ [TABLA_A_TX_1_2_ATEST_REFCTRL] = TABLA_A_TX_1_2_ATEST_REFCTRL__POR,
+ [TABLA_A_TX_1_2_TEST_CTL] = TABLA_A_TX_1_2_TEST_CTL__POR,
+ [TABLA_A_TX_1_2_TEST_BLOCK_EN] = TABLA_A_TX_1_2_TEST_BLOCK_EN__POR,
+ [TABLA_A_TX_1_2_TXFE_CLKDIV] = TABLA_A_TX_1_2_TXFE_CLKDIV__POR,
+ [TABLA_A_TX_1_2_SAR_ERR_CH1] = TABLA_A_TX_1_2_SAR_ERR_CH1__POR,
+ [TABLA_A_TX_1_2_SAR_ERR_CH2] = TABLA_A_TX_1_2_SAR_ERR_CH2__POR,
+ [TABLA_A_TX_3_4_EN] = TABLA_A_TX_3_4_EN__POR,
+ [TABLA_A_TX_3_4_TEST_EN] = TABLA_A_TX_3_4_TEST_EN__POR,
+ [TABLA_A_TX_3_4_ADC_CH3] = TABLA_A_TX_3_4_ADC_CH3__POR,
+ [TABLA_A_TX_3_4_ADC_CH4] = TABLA_A_TX_3_4_ADC_CH4__POR,
+ [TABLA_A_TX_3_4_ATEST_REFCTRL] = TABLA_A_TX_3_4_ATEST_REFCTRL__POR,
+ [TABLA_A_TX_3_4_TEST_CTL] = TABLA_A_TX_3_4_TEST_CTL__POR,
+ [TABLA_A_TX_3_4_TEST_BLOCK_EN] = TABLA_A_TX_3_4_TEST_BLOCK_EN__POR,
+ [TABLA_A_TX_3_4_TXFE_CKDIV] = TABLA_A_TX_3_4_TXFE_CKDIV__POR,
+ [TABLA_A_TX_3_4_SAR_ERR_CH3] = TABLA_A_TX_3_4_SAR_ERR_CH3__POR,
+ [TABLA_A_TX_3_4_SAR_ERR_CH4] = TABLA_A_TX_3_4_SAR_ERR_CH4__POR,
+ [TABLA_A_TX_5_6_EN] = TABLA_A_TX_5_6_EN__POR,
+ [TABLA_A_TX_5_6_TEST_EN] = TABLA_A_TX_5_6_TEST_EN__POR,
+ [TABLA_A_TX_5_6_ADC_CH5] = TABLA_A_TX_5_6_ADC_CH5__POR,
+ [TABLA_A_TX_5_6_ADC_CH6] = TABLA_A_TX_5_6_ADC_CH6__POR,
+ [TABLA_A_TX_5_6_ATEST_REFCTRL] = TABLA_A_TX_5_6_ATEST_REFCTRL__POR,
+ [TABLA_A_TX_5_6_TEST_CTL] = TABLA_A_TX_5_6_TEST_CTL__POR,
+ [TABLA_A_TX_5_6_TEST_BLOCK_EN] = TABLA_A_TX_5_6_TEST_BLOCK_EN__POR,
+ [TABLA_A_TX_5_6_TXFE_CKDIV] = TABLA_A_TX_5_6_TXFE_CKDIV__POR,
+ [TABLA_A_TX_5_6_SAR_ERR_CH5] = TABLA_A_TX_5_6_SAR_ERR_CH5__POR,
+ [TABLA_A_TX_5_6_SAR_ERR_CH6] = TABLA_A_TX_5_6_SAR_ERR_CH6__POR,
+ [TABLA_A_TX_7_MBHC_EN] = TABLA_A_TX_7_MBHC_EN__POR,
+ [TABLA_A_TX_7_MBHC_ATEST_REFCTRL] =
+ TABLA_A_TX_7_MBHC_ATEST_REFCTRL__POR,
+ [TABLA_A_TX_7_MBHC_ADC] = TABLA_A_TX_7_MBHC_ADC__POR,
+ [TABLA_A_TX_7_MBHC_TEST_CTL] = TABLA_A_TX_7_MBHC_TEST_CTL__POR,
+ [TABLA_A_TX_7_MBHC_SAR_ERR] = TABLA_A_TX_7_MBHC_SAR_ERR__POR,
+ [TABLA_A_TX_7_TXFE_CLKDIV] = TABLA_A_TX_7_TXFE_CLKDIV__POR,
+ [TABLA_A_AUX_COM_CTL] = TABLA_A_AUX_COM_CTL__POR,
+ [TABLA_A_AUX_COM_ATEST] = TABLA_A_AUX_COM_ATEST__POR,
+ [TABLA_A_AUX_L_EN] = TABLA_A_AUX_L_EN__POR,
+ [TABLA_A_AUX_L_GAIN] = TABLA_A_AUX_L_GAIN__POR,
+ [TABLA_A_AUX_L_PA_CONN] = TABLA_A_AUX_L_PA_CONN__POR,
+ [TABLA_A_AUX_L_PA_CONN_INV] = TABLA_A_AUX_L_PA_CONN_INV__POR,
+ [TABLA_A_AUX_R_EN] = TABLA_A_AUX_R_EN__POR,
+ [TABLA_A_AUX_R_GAIN] = TABLA_A_AUX_R_GAIN__POR,
+ [TABLA_A_AUX_R_PA_CONN] = TABLA_A_AUX_R_PA_CONN__POR,
+ [TABLA_A_AUX_R_PA_CONN_INV] = TABLA_A_AUX_R_PA_CONN_INV__POR,
+ [TABLA_A_CP_EN] = TABLA_A_CP_EN__POR,
+ [TABLA_A_CP_CLK] = TABLA_A_CP_CLK__POR,
+ [TABLA_A_CP_STATIC] = TABLA_A_CP_STATIC__POR,
+ [TABLA_A_CP_DCC1] = TABLA_A_CP_DCC1__POR,
+ [TABLA_A_CP_DCC3] = TABLA_A_CP_DCC3__POR,
+ [TABLA_A_CP_ATEST] = TABLA_A_CP_ATEST__POR,
+ [TABLA_A_CP_DTEST] = TABLA_A_CP_DTEST__POR,
+ [TABLA_A_RX_COM_TIMER_DIV] = TABLA_A_RX_COM_TIMER_DIV__POR,
+ [TABLA_A_RX_COM_OCP_CTL] = TABLA_A_RX_COM_OCP_CTL__POR,
+ [TABLA_A_RX_COM_OCP_COUNT] = TABLA_A_RX_COM_OCP_COUNT__POR,
+ [TABLA_A_RX_COM_DAC_CTL] = TABLA_A_RX_COM_DAC_CTL__POR,
+ [TABLA_A_RX_COM_BIAS] = TABLA_A_RX_COM_BIAS__POR,
+ [TABLA_A_RX_HPH_BIAS_PA] = TABLA_A_RX_HPH_BIAS_PA__POR,
+ [TABLA_A_RX_HPH_BIAS_LDO] = TABLA_A_RX_HPH_BIAS_LDO__POR,
+ [TABLA_A_RX_HPH_BIAS_CNP] = TABLA_A_RX_HPH_BIAS_CNP__POR,
+ [TABLA_A_RX_HPH_BIAS_WG] = TABLA_A_RX_HPH_BIAS_WG__POR,
+ [TABLA_A_RX_HPH_OCP_CTL] = TABLA_A_RX_HPH_OCP_CTL__POR,
+ [TABLA_A_RX_HPH_CNP_EN] = TABLA_A_RX_HPH_CNP_EN__POR,
+ [TABLA_A_RX_HPH_CNP_WG_CTL] = TABLA_A_RX_HPH_CNP_WG_CTL__POR,
+ [TABLA_A_RX_HPH_CNP_WG_TIME] = TABLA_A_RX_HPH_CNP_WG_TIME__POR,
+ [TABLA_A_RX_HPH_L_GAIN] = TABLA_A_RX_HPH_L_GAIN__POR,
+ [TABLA_A_RX_HPH_L_TEST] = TABLA_A_RX_HPH_L_TEST__POR,
+ [TABLA_A_RX_HPH_L_PA_CTL] = TABLA_A_RX_HPH_L_PA_CTL__POR,
+ [TABLA_A_RX_HPH_L_DAC_CTL] = TABLA_A_RX_HPH_L_DAC_CTL__POR,
+ [TABLA_A_RX_HPH_L_ATEST] = TABLA_A_RX_HPH_L_ATEST__POR,
+ [TABLA_A_RX_HPH_L_STATUS] = TABLA_A_RX_HPH_L_STATUS__POR,
+ [TABLA_A_RX_HPH_R_GAIN] = TABLA_A_RX_HPH_R_GAIN__POR,
+ [TABLA_A_RX_HPH_R_TEST] = TABLA_A_RX_HPH_R_TEST__POR,
+ [TABLA_A_RX_HPH_R_PA_CTL] = TABLA_A_RX_HPH_R_PA_CTL__POR,
+ [TABLA_A_RX_HPH_R_DAC_CTL] = TABLA_A_RX_HPH_R_DAC_CTL__POR,
+ [TABLA_A_RX_HPH_R_ATEST] = TABLA_A_RX_HPH_R_ATEST__POR,
+ [TABLA_A_RX_HPH_R_STATUS] = TABLA_A_RX_HPH_R_STATUS__POR,
+ [TABLA_A_RX_EAR_BIAS_PA] = TABLA_A_RX_EAR_BIAS_PA__POR,
+ [TABLA_A_RX_EAR_BIAS_CMBUFF] = TABLA_A_RX_EAR_BIAS_CMBUFF__POR,
+ [TABLA_A_RX_EAR_EN] = TABLA_A_RX_EAR_EN__POR,
+ [TABLA_A_RX_EAR_GAIN] = TABLA_A_RX_EAR_GAIN__POR,
+ [TABLA_A_RX_EAR_CMBUFF] = TABLA_A_RX_EAR_CMBUFF__POR,
+ [TABLA_A_RX_EAR_ICTL] = TABLA_A_RX_EAR_ICTL__POR,
+ [TABLA_A_RX_EAR_CCOMP] = TABLA_A_RX_EAR_CCOMP__POR,
+ [TABLA_A_RX_EAR_VCM] = TABLA_A_RX_EAR_VCM__POR,
+ [TABLA_A_RX_EAR_CNP] = TABLA_A_RX_EAR_CNP__POR,
+ [TABLA_A_RX_EAR_ATEST] = TABLA_A_RX_EAR_ATEST__POR,
+ [TABLA_A_RX_EAR_STATUS] = TABLA_A_RX_EAR_STATUS__POR,
+ [TABLA_A_RX_LINE_BIAS_PA] = TABLA_A_RX_LINE_BIAS_PA__POR,
+ [TABLA_A_RX_LINE_BIAS_DAC] = TABLA_A_RX_LINE_BIAS_DAC__POR,
+ [TABLA_A_RX_LINE_BIAS_CNP] = TABLA_A_RX_LINE_BIAS_CNP__POR,
+ [TABLA_A_RX_LINE_COM] = TABLA_A_RX_LINE_COM__POR,
+ [TABLA_A_RX_LINE_CNP_EN] = TABLA_A_RX_LINE_CNP_EN__POR,
+ [TABLA_A_RX_LINE_CNP_WG_CTL] = TABLA_A_RX_LINE_CNP_WG_CTL__POR,
+ [TABLA_A_RX_LINE_CNP_WG_TIME] = TABLA_A_RX_LINE_CNP_WG_TIME__POR,
+ [TABLA_A_RX_LINE_1_GAIN] = TABLA_A_RX_LINE_1_GAIN__POR,
+ [TABLA_A_RX_LINE_1_TEST] = TABLA_A_RX_LINE_1_TEST__POR,
+ [TABLA_A_RX_LINE_1_DAC_CTL] = TABLA_A_RX_LINE_1_DAC_CTL__POR,
+ [TABLA_A_RX_LINE_1_STATUS] = TABLA_A_RX_LINE_1_STATUS__POR,
+ [TABLA_A_RX_LINE_2_GAIN] = TABLA_A_RX_LINE_2_GAIN__POR,
+ [TABLA_A_RX_LINE_2_TEST] = TABLA_A_RX_LINE_2_TEST__POR,
+ [TABLA_A_RX_LINE_2_DAC_CTL] = TABLA_A_RX_LINE_2_DAC_CTL__POR,
+ [TABLA_A_RX_LINE_2_STATUS] = TABLA_A_RX_LINE_2_STATUS__POR,
+ [TABLA_A_RX_LINE_3_GAIN] = TABLA_A_RX_LINE_3_GAIN__POR,
+ [TABLA_A_RX_LINE_3_TEST] = TABLA_A_RX_LINE_3_TEST__POR,
+ [TABLA_A_RX_LINE_3_DAC_CTL] = TABLA_A_RX_LINE_3_DAC_CTL__POR,
+ [TABLA_A_RX_LINE_3_STATUS] = TABLA_A_RX_LINE_3_STATUS__POR,
+ [TABLA_A_RX_LINE_4_GAIN] = TABLA_A_RX_LINE_4_GAIN__POR,
+ [TABLA_A_RX_LINE_4_TEST] = TABLA_A_RX_LINE_4_TEST__POR,
+ [TABLA_A_RX_LINE_4_DAC_CTL] = TABLA_A_RX_LINE_4_DAC_CTL__POR,
+ [TABLA_A_RX_LINE_4_STATUS] = TABLA_A_RX_LINE_4_STATUS__POR,
+ [TABLA_A_RX_LINE_5_GAIN] = TABLA_A_RX_LINE_5_GAIN__POR,
+ [TABLA_A_RX_LINE_5_TEST] = TABLA_A_RX_LINE_5_TEST__POR,
+ [TABLA_A_RX_LINE_5_DAC_CTL] = TABLA_A_RX_LINE_5_DAC_CTL__POR,
+ [TABLA_A_RX_LINE_5_STATUS] = TABLA_A_RX_LINE_5_STATUS__POR,
+ [TABLA_A_RX_LINE_CNP_DBG] = TABLA_A_RX_LINE_CNP_DBG__POR,
+ [TABLA_A_MBHC_HPH] = TABLA_A_MBHC_HPH__POR,
+ [TABLA_A_CONFIG_MODE_FREQ] = TABLA_A_CONFIG_MODE_FREQ__POR,
+ [TABLA_A_CONFIG_MODE_TEST] = TABLA_A_CONFIG_MODE_TEST__POR,
+ [TABLA_A_CONFIG_MODE_STATUS] = TABLA_A_CONFIG_MODE_STATUS__POR,
+ [TABLA_A_CONFIG_MODE_TUNER] = TABLA_A_CONFIG_MODE_TUNER__POR,
+ [TABLA_A_CDC_TX1_VOL_CTL_TIMER] = TABLA_A_CDC_TX1_VOL_CTL_TIMER__POR,
+ [TABLA_A_CDC_TX2_VOL_CTL_TIMER] = TABLA_A_CDC_TX2_VOL_CTL_TIMER__POR,
+ [TABLA_A_CDC_TX3_VOL_CTL_TIMER] = TABLA_A_CDC_TX3_VOL_CTL_TIMER__POR,
+ [TABLA_A_CDC_TX4_VOL_CTL_TIMER] = TABLA_A_CDC_TX4_VOL_CTL_TIMER__POR,
+ [TABLA_A_CDC_TX5_VOL_CTL_TIMER] = TABLA_A_CDC_TX5_VOL_CTL_TIMER__POR,
+ [TABLA_A_CDC_TX6_VOL_CTL_TIMER] = TABLA_A_CDC_TX6_VOL_CTL_TIMER__POR,
+ [TABLA_A_CDC_TX7_VOL_CTL_TIMER] = TABLA_A_CDC_TX7_VOL_CTL_TIMER__POR,
+ [TABLA_A_CDC_TX8_VOL_CTL_TIMER] = TABLA_A_CDC_TX8_VOL_CTL_TIMER__POR,
+ [TABLA_A_CDC_TX9_VOL_CTL_TIMER] = TABLA_A_CDC_TX9_VOL_CTL_TIMER__POR,
+ [TABLA_A_CDC_TX10_VOL_CTL_TIMER] = TABLA_A_CDC_TX10_VOL_CTL_TIMER__POR,
+ [TABLA_A_CDC_TX1_VOL_CTL_GAIN] = TABLA_A_CDC_TX1_VOL_CTL_GAIN__POR,
+ [TABLA_A_CDC_TX2_VOL_CTL_GAIN] = TABLA_A_CDC_TX2_VOL_CTL_GAIN__POR,
+ [TABLA_A_CDC_TX3_VOL_CTL_GAIN] = TABLA_A_CDC_TX3_VOL_CTL_GAIN__POR,
+ [TABLA_A_CDC_TX4_VOL_CTL_GAIN] = TABLA_A_CDC_TX4_VOL_CTL_GAIN__POR,
+ [TABLA_A_CDC_TX5_VOL_CTL_GAIN] = TABLA_A_CDC_TX5_VOL_CTL_GAIN__POR,
+ [TABLA_A_CDC_TX6_VOL_CTL_GAIN] = TABLA_A_CDC_TX6_VOL_CTL_GAIN__POR,
+ [TABLA_A_CDC_TX7_VOL_CTL_GAIN] = TABLA_A_CDC_TX7_VOL_CTL_GAIN__POR,
+ [TABLA_A_CDC_TX8_VOL_CTL_GAIN] = TABLA_A_CDC_TX8_VOL_CTL_GAIN__POR,
+ [TABLA_A_CDC_TX9_VOL_CTL_GAIN] = TABLA_A_CDC_TX9_VOL_CTL_GAIN__POR,
+ [TABLA_A_CDC_TX10_VOL_CTL_GAIN] = TABLA_A_CDC_TX10_VOL_CTL_GAIN__POR,
+ [TABLA_A_CDC_TX1_VOL_CTL_CFG] = TABLA_A_CDC_TX1_VOL_CTL_CFG__POR,
+ [TABLA_A_CDC_TX2_VOL_CTL_CFG] = TABLA_A_CDC_TX2_VOL_CTL_CFG__POR,
+ [TABLA_A_CDC_TX3_VOL_CTL_CFG] = TABLA_A_CDC_TX3_VOL_CTL_CFG__POR,
+ [TABLA_A_CDC_TX4_VOL_CTL_CFG] = TABLA_A_CDC_TX4_VOL_CTL_CFG__POR,
+ [TABLA_A_CDC_TX5_VOL_CTL_CFG] = TABLA_A_CDC_TX5_VOL_CTL_CFG__POR,
+ [TABLA_A_CDC_TX6_VOL_CTL_CFG] = TABLA_A_CDC_TX6_VOL_CTL_CFG__POR,
+ [TABLA_A_CDC_TX7_VOL_CTL_CFG] = TABLA_A_CDC_TX7_VOL_CTL_CFG__POR,
+ [TABLA_A_CDC_TX8_VOL_CTL_CFG] = TABLA_A_CDC_TX8_VOL_CTL_CFG__POR,
+ [TABLA_A_CDC_TX9_VOL_CTL_CFG] = TABLA_A_CDC_TX9_VOL_CTL_CFG__POR,
+ [TABLA_A_CDC_TX10_VOL_CTL_CFG] = TABLA_A_CDC_TX10_VOL_CTL_CFG__POR,
+ [TABLA_A_CDC_TX1_MUX_CTL] = TABLA_A_CDC_TX1_MUX_CTL__POR,
+ [TABLA_A_CDC_TX2_MUX_CTL] = TABLA_A_CDC_TX2_MUX_CTL__POR,
+ [TABLA_A_CDC_TX3_MUX_CTL] = TABLA_A_CDC_TX3_MUX_CTL__POR,
+ [TABLA_A_CDC_TX4_MUX_CTL] = TABLA_A_CDC_TX4_MUX_CTL__POR,
+ [TABLA_A_CDC_TX5_MUX_CTL] = TABLA_A_CDC_TX5_MUX_CTL__POR,
+ [TABLA_A_CDC_TX6_MUX_CTL] = TABLA_A_CDC_TX6_MUX_CTL__POR,
+ [TABLA_A_CDC_TX7_MUX_CTL] = TABLA_A_CDC_TX7_MUX_CTL__POR,
+ [TABLA_A_CDC_TX8_MUX_CTL] = TABLA_A_CDC_TX8_MUX_CTL__POR,
+ [TABLA_A_CDC_TX9_MUX_CTL] = TABLA_A_CDC_TX9_MUX_CTL__POR,
+ [TABLA_A_CDC_TX10_MUX_CTL] = TABLA_A_CDC_TX10_MUX_CTL__POR,
+ [TABLA_A_CDC_TX1_CLK_FS_CTL] = TABLA_A_CDC_TX1_CLK_FS_CTL__POR,
+ [TABLA_A_CDC_TX2_CLK_FS_CTL] = TABLA_A_CDC_TX2_CLK_FS_CTL__POR,
+ [TABLA_A_CDC_TX3_CLK_FS_CTL] = TABLA_A_CDC_TX3_CLK_FS_CTL__POR,
+ [TABLA_A_CDC_TX4_CLK_FS_CTL] = TABLA_A_CDC_TX4_CLK_FS_CTL__POR,
+ [TABLA_A_CDC_TX5_CLK_FS_CTL] = TABLA_A_CDC_TX5_CLK_FS_CTL__POR,
+ [TABLA_A_CDC_TX6_CLK_FS_CTL] = TABLA_A_CDC_TX6_CLK_FS_CTL__POR,
+ [TABLA_A_CDC_TX7_CLK_FS_CTL] = TABLA_A_CDC_TX7_CLK_FS_CTL__POR,
+ [TABLA_A_CDC_TX8_CLK_FS_CTL] = TABLA_A_CDC_TX8_CLK_FS_CTL__POR,
+ [TABLA_A_CDC_TX9_CLK_FS_CTL] = TABLA_A_CDC_TX9_CLK_FS_CTL__POR,
+ [TABLA_A_CDC_TX10_CLK_FS_CTL] = TABLA_A_CDC_TX10_CLK_FS_CTL__POR,
+ [TABLA_A_CDC_TX1_DMIC_CTL] = TABLA_A_CDC_TX1_DMIC_CTL__POR,
+ [TABLA_A_CDC_TX2_DMIC_CTL] = TABLA_A_CDC_TX2_DMIC_CTL__POR,
+ [TABLA_A_CDC_TX3_DMIC_CTL] = TABLA_A_CDC_TX3_DMIC_CTL__POR,
+ [TABLA_A_CDC_TX4_DMIC_CTL] = TABLA_A_CDC_TX4_DMIC_CTL__POR,
+ [TABLA_A_CDC_TX5_DMIC_CTL] = TABLA_A_CDC_TX5_DMIC_CTL__POR,
+ [TABLA_A_CDC_TX6_DMIC_CTL] = TABLA_A_CDC_TX6_DMIC_CTL__POR,
+ [TABLA_A_CDC_TX7_DMIC_CTL] = TABLA_A_CDC_TX7_DMIC_CTL__POR,
+ [TABLA_A_CDC_TX8_DMIC_CTL] = TABLA_A_CDC_TX8_DMIC_CTL__POR,
+ [TABLA_A_CDC_TX9_DMIC_CTL] = TABLA_A_CDC_TX9_DMIC_CTL__POR,
+ [TABLA_A_CDC_TX10_DMIC_CTL] = TABLA_A_CDC_TX10_DMIC_CTL__POR,
+ [TABLA_A_CDC_SRC1_PDA_CFG] = TABLA_A_CDC_SRC1_PDA_CFG__POR,
+ [TABLA_A_CDC_SRC2_PDA_CFG] = TABLA_A_CDC_SRC2_PDA_CFG__POR,
+ [TABLA_A_CDC_SRC1_FS_CTL] = TABLA_A_CDC_SRC1_FS_CTL__POR,
+ [TABLA_A_CDC_SRC2_FS_CTL] = TABLA_A_CDC_SRC2_FS_CTL__POR,
+ [TABLA_A_CDC_RX1_B1_CTL] = TABLA_A_CDC_RX1_B1_CTL__POR,
+ [TABLA_A_CDC_RX2_B1_CTL] = TABLA_A_CDC_RX2_B1_CTL__POR,
+ [TABLA_A_CDC_RX3_B1_CTL] = TABLA_A_CDC_RX3_B1_CTL__POR,
+ [TABLA_A_CDC_RX4_B1_CTL] = TABLA_A_CDC_RX4_B1_CTL__POR,
+ [TABLA_A_CDC_RX5_B1_CTL] = TABLA_A_CDC_RX5_B1_CTL__POR,
+ [TABLA_A_CDC_RX6_B1_CTL] = TABLA_A_CDC_RX6_B1_CTL__POR,
+ [TABLA_A_CDC_RX7_B1_CTL] = TABLA_A_CDC_RX7_B1_CTL__POR,
+ [TABLA_A_CDC_RX1_B2_CTL] = TABLA_A_CDC_RX1_B2_CTL__POR,
+ [TABLA_A_CDC_RX2_B2_CTL] = TABLA_A_CDC_RX2_B2_CTL__POR,
+ [TABLA_A_CDC_RX3_B2_CTL] = TABLA_A_CDC_RX3_B2_CTL__POR,
+ [TABLA_A_CDC_RX4_B2_CTL] = TABLA_A_CDC_RX4_B2_CTL__POR,
+ [TABLA_A_CDC_RX5_B2_CTL] = TABLA_A_CDC_RX5_B2_CTL__POR,
+ [TABLA_A_CDC_RX6_B2_CTL] = TABLA_A_CDC_RX6_B2_CTL__POR,
+ [TABLA_A_CDC_RX7_B2_CTL] = TABLA_A_CDC_RX7_B2_CTL__POR,
+ [TABLA_A_CDC_RX1_B3_CTL] = TABLA_A_CDC_RX1_B3_CTL__POR,
+ [TABLA_A_CDC_RX2_B3_CTL] = TABLA_A_CDC_RX2_B3_CTL__POR,
+ [TABLA_A_CDC_RX3_B3_CTL] = TABLA_A_CDC_RX3_B3_CTL__POR,
+ [TABLA_A_CDC_RX4_B3_CTL] = TABLA_A_CDC_RX4_B3_CTL__POR,
+ [TABLA_A_CDC_RX5_B3_CTL] = TABLA_A_CDC_RX5_B3_CTL__POR,
+ [TABLA_A_CDC_RX6_B3_CTL] = TABLA_A_CDC_RX6_B3_CTL__POR,
+ [TABLA_A_CDC_RX7_B3_CTL] = TABLA_A_CDC_RX7_B3_CTL__POR,
+ [TABLA_A_CDC_RX1_B4_CTL] = TABLA_A_CDC_RX1_B4_CTL__POR,
+ [TABLA_A_CDC_RX2_B4_CTL] = TABLA_A_CDC_RX2_B4_CTL__POR,
+ [TABLA_A_CDC_RX3_B4_CTL] = TABLA_A_CDC_RX3_B4_CTL__POR,
+ [TABLA_A_CDC_RX4_B4_CTL] = TABLA_A_CDC_RX4_B4_CTL__POR,
+ [TABLA_A_CDC_RX5_B4_CTL] = TABLA_A_CDC_RX5_B4_CTL__POR,
+ [TABLA_A_CDC_RX6_B4_CTL] = TABLA_A_CDC_RX6_B4_CTL__POR,
+ [TABLA_A_CDC_RX7_B4_CTL] = TABLA_A_CDC_RX7_B4_CTL__POR,
+ [TABLA_A_CDC_RX1_B5_CTL] = TABLA_A_CDC_RX1_B5_CTL__POR,
+ [TABLA_A_CDC_RX2_B5_CTL] = TABLA_A_CDC_RX2_B5_CTL__POR,
+ [TABLA_A_CDC_RX3_B5_CTL] = TABLA_A_CDC_RX3_B5_CTL__POR,
+ [TABLA_A_CDC_RX4_B5_CTL] = TABLA_A_CDC_RX4_B5_CTL__POR,
+ [TABLA_A_CDC_RX5_B5_CTL] = TABLA_A_CDC_RX5_B5_CTL__POR,
+ [TABLA_A_CDC_RX6_B5_CTL] = TABLA_A_CDC_RX6_B5_CTL__POR,
+ [TABLA_A_CDC_RX7_B5_CTL] = TABLA_A_CDC_RX7_B5_CTL__POR,
+ [TABLA_A_CDC_RX1_B6_CTL] = TABLA_A_CDC_RX1_B6_CTL__POR,
+ [TABLA_A_CDC_RX2_B6_CTL] = TABLA_A_CDC_RX2_B6_CTL__POR,
+ [TABLA_A_CDC_RX3_B6_CTL] = TABLA_A_CDC_RX3_B6_CTL__POR,
+ [TABLA_A_CDC_RX4_B6_CTL] = TABLA_A_CDC_RX4_B6_CTL__POR,
+ [TABLA_A_CDC_RX5_B6_CTL] = TABLA_A_CDC_RX5_B6_CTL__POR,
+ [TABLA_A_CDC_RX6_B6_CTL] = TABLA_A_CDC_RX6_B6_CTL__POR,
+ [TABLA_A_CDC_RX7_B6_CTL] = TABLA_A_CDC_RX7_B6_CTL__POR,
+ [TABLA_A_CDC_RX1_VOL_CTL_B1_CTL] = TABLA_A_CDC_RX1_VOL_CTL_B1_CTL__POR,
+ [TABLA_A_CDC_RX2_VOL_CTL_B1_CTL] = TABLA_A_CDC_RX2_VOL_CTL_B1_CTL__POR,
+ [TABLA_A_CDC_RX3_VOL_CTL_B1_CTL] = TABLA_A_CDC_RX3_VOL_CTL_B1_CTL__POR,
+ [TABLA_A_CDC_RX4_VOL_CTL_B1_CTL] = TABLA_A_CDC_RX4_VOL_CTL_B1_CTL__POR,
+ [TABLA_A_CDC_RX5_VOL_CTL_B1_CTL] = TABLA_A_CDC_RX5_VOL_CTL_B1_CTL__POR,
+ [TABLA_A_CDC_RX6_VOL_CTL_B1_CTL] = TABLA_A_CDC_RX6_VOL_CTL_B1_CTL__POR,
+ [TABLA_A_CDC_RX7_VOL_CTL_B1_CTL] = TABLA_A_CDC_RX7_VOL_CTL_B1_CTL__POR,
+ [TABLA_A_CDC_RX1_VOL_CTL_B2_CTL] = TABLA_A_CDC_RX1_VOL_CTL_B2_CTL__POR,
+ [TABLA_A_CDC_RX2_VOL_CTL_B2_CTL] = TABLA_A_CDC_RX2_VOL_CTL_B2_CTL__POR,
+ [TABLA_A_CDC_RX3_VOL_CTL_B2_CTL] = TABLA_A_CDC_RX3_VOL_CTL_B2_CTL__POR,
+ [TABLA_A_CDC_RX4_VOL_CTL_B2_CTL] = TABLA_A_CDC_RX4_VOL_CTL_B2_CTL__POR,
+ [TABLA_A_CDC_RX5_VOL_CTL_B2_CTL] = TABLA_A_CDC_RX5_VOL_CTL_B2_CTL__POR,
+ [TABLA_A_CDC_RX6_VOL_CTL_B2_CTL] = TABLA_A_CDC_RX6_VOL_CTL_B2_CTL__POR,
+ [TABLA_A_CDC_RX7_VOL_CTL_B2_CTL] = TABLA_A_CDC_RX7_VOL_CTL_B2_CTL__POR,
+ [TABLA_A_CDC_CLK_RX_RESET_CTL] = TABLA_A_CDC_CLK_RX_RESET_CTL__POR,
+ [TABLA_A_CDC_CLK_TX_RESET_B1_CTL] =
+ TABLA_A_CDC_CLK_TX_RESET_B1_CTL__POR,
+ [TABLA_A_CDC_CLK_TX_RESET_B2_CTL] =
+ TABLA_A_CDC_CLK_TX_RESET_B2_CTL__POR,
+ [TABLA_A_CDC_CLK_DMIC_CTL] = TABLA_A_CDC_CLK_DMIC_CTL__POR,
+ [TABLA_A_CDC_CLK_RX_I2S_CTL] = TABLA_A_CDC_CLK_RX_I2S_CTL__POR,
+ [TABLA_A_CDC_CLK_TX_I2S_CTL] = TABLA_A_CDC_CLK_TX_I2S_CTL__POR,
+ [TABLA_A_CDC_CLK_OTHR_RESET_CTL] = TABLA_A_CDC_CLK_OTHR_RESET_CTL__POR,
+ [TABLA_A_CDC_CLK_TX_CLK_EN_B1_CTL] =
+ TABLA_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR,
+ [TABLA_A_CDC_CLK_TX_CLK_EN_B2_CTL] =
+ TABLA_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR,
+ [TABLA_A_CDC_CLK_OTHR_CTL] = TABLA_A_CDC_CLK_OTHR_CTL__POR,
+ [TABLA_A_CDC_CLK_RDAC_CLK_EN_CTL] =
+ TABLA_A_CDC_CLK_RDAC_CLK_EN_CTL__POR,
+ [TABLA_A_CDC_CLK_RX_B1_CTL] = TABLA_A_CDC_CLK_RX_B1_CTL__POR,
+ [TABLA_A_CDC_CLK_RX_B2_CTL] = TABLA_A_CDC_CLK_RX_B2_CTL__POR,
+ [TABLA_A_CDC_CLK_MCLK_CTL] = TABLA_A_CDC_CLK_MCLK_CTL__POR,
+ [TABLA_A_CDC_CLK_PDM_CTL] = TABLA_A_CDC_CLK_PDM_CTL__POR,
+ [TABLA_A_CDC_CLK_SD_CTL] = TABLA_A_CDC_CLK_SD_CTL__POR,
+ [TABLA_A_CDC_CLSG_FREQ_THRESH_B1_CTL] =
+ TABLA_A_CDC_CLSG_FREQ_THRESH_B1_CTL__POR,
+ [TABLA_A_CDC_CLSG_FREQ_THRESH_B2_CTL] =
+ TABLA_A_CDC_CLSG_FREQ_THRESH_B2_CTL__POR,
+ [TABLA_A_CDC_CLSG_FREQ_THRESH_B3_CTL] =
+ TABLA_A_CDC_CLSG_FREQ_THRESH_B3_CTL__POR,
+ [TABLA_A_CDC_CLSG_FREQ_THRESH_B4_CTL] =
+ TABLA_A_CDC_CLSG_FREQ_THRESH_B4_CTL__POR,
+ [TABLA_A_CDC_CLSG_GAIN_THRESH_CTL] =
+ TABLA_A_CDC_CLSG_GAIN_THRESH_CTL__POR,
+ [TABLA_A_CDC_CLSG_TIMER_B1_CFG] = TABLA_A_CDC_CLSG_TIMER_B1_CFG__POR,
+ [TABLA_A_CDC_CLSG_TIMER_B2_CFG] = TABLA_A_CDC_CLSG_TIMER_B2_CFG__POR,
+ [TABLA_A_CDC_CLSG_CTL] = TABLA_A_CDC_CLSG_CTL__POR,
+ [TABLA_A_CDC_IIR1_GAIN_B1_CTL] = TABLA_A_CDC_IIR1_GAIN_B1_CTL__POR,
+ [TABLA_A_CDC_IIR2_GAIN_B1_CTL] = TABLA_A_CDC_IIR2_GAIN_B1_CTL__POR,
+ [TABLA_A_CDC_IIR1_GAIN_B2_CTL] = TABLA_A_CDC_IIR1_GAIN_B2_CTL__POR,
+ [TABLA_A_CDC_IIR2_GAIN_B2_CTL] = TABLA_A_CDC_IIR2_GAIN_B2_CTL__POR,
+ [TABLA_A_CDC_IIR1_GAIN_B3_CTL] = TABLA_A_CDC_IIR1_GAIN_B3_CTL__POR,
+ [TABLA_A_CDC_IIR2_GAIN_B3_CTL] = TABLA_A_CDC_IIR2_GAIN_B3_CTL__POR,
+ [TABLA_A_CDC_IIR1_GAIN_B4_CTL] = TABLA_A_CDC_IIR1_GAIN_B4_CTL__POR,
+ [TABLA_A_CDC_IIR2_GAIN_B4_CTL] = TABLA_A_CDC_IIR2_GAIN_B4_CTL__POR,
+ [TABLA_A_CDC_IIR1_GAIN_B5_CTL] = TABLA_A_CDC_IIR1_GAIN_B5_CTL__POR,
+ [TABLA_A_CDC_IIR2_GAIN_B5_CTL] = TABLA_A_CDC_IIR2_GAIN_B5_CTL__POR,
+ [TABLA_A_CDC_IIR1_GAIN_B6_CTL] = TABLA_A_CDC_IIR1_GAIN_B6_CTL__POR,
+ [TABLA_A_CDC_IIR2_GAIN_B6_CTL] = TABLA_A_CDC_IIR2_GAIN_B6_CTL__POR,
+ [TABLA_A_CDC_IIR1_GAIN_B7_CTL] = TABLA_A_CDC_IIR1_GAIN_B7_CTL__POR,
+ [TABLA_A_CDC_IIR2_GAIN_B7_CTL] = TABLA_A_CDC_IIR2_GAIN_B7_CTL__POR,
+ [TABLA_A_CDC_IIR1_GAIN_B8_CTL] = TABLA_A_CDC_IIR1_GAIN_B8_CTL__POR,
+ [TABLA_A_CDC_IIR2_GAIN_B8_CTL] = TABLA_A_CDC_IIR2_GAIN_B8_CTL__POR,
+ [TABLA_A_CDC_IIR1_CTL] = TABLA_A_CDC_IIR1_CTL__POR,
+ [TABLA_A_CDC_IIR2_CTL] = TABLA_A_CDC_IIR2_CTL__POR,
+ [TABLA_A_CDC_IIR1_GAIN_TIMER_CTL] =
+ TABLA_A_CDC_IIR1_GAIN_TIMER_CTL__POR,
+ [TABLA_A_CDC_IIR2_GAIN_TIMER_CTL] =
+ TABLA_A_CDC_IIR2_GAIN_TIMER_CTL__POR,
+ [TABLA_A_CDC_IIR1_COEF_B1_CTL] = TABLA_A_CDC_IIR1_COEF_B1_CTL__POR,
+ [TABLA_A_CDC_IIR2_COEF_B1_CTL] = TABLA_A_CDC_IIR2_COEF_B1_CTL__POR,
+ [TABLA_A_CDC_IIR1_COEF_B2_CTL] = TABLA_A_CDC_IIR1_COEF_B2_CTL__POR,
+ [TABLA_A_CDC_IIR2_COEF_B2_CTL] = TABLA_A_CDC_IIR2_COEF_B2_CTL__POR,
+ [TABLA_A_CDC_IIR1_COEF_B3_CTL] = TABLA_A_CDC_IIR1_COEF_B3_CTL__POR,
+ [TABLA_A_CDC_IIR2_COEF_B3_CTL] = TABLA_A_CDC_IIR2_COEF_B3_CTL__POR,
+ [TABLA_A_CDC_IIR1_COEF_B4_CTL] = TABLA_A_CDC_IIR1_COEF_B4_CTL__POR,
+ [TABLA_A_CDC_IIR2_COEF_B4_CTL] = TABLA_A_CDC_IIR2_COEF_B4_CTL__POR,
+ [TABLA_A_CDC_IIR1_COEF_B5_CTL] = TABLA_A_CDC_IIR1_COEF_B5_CTL__POR,
+ [TABLA_A_CDC_IIR2_COEF_B5_CTL] = TABLA_A_CDC_IIR2_COEF_B5_CTL__POR,
+ [TABLA_A_CDC_TOP_GAIN_UPDATE] = TABLA_A_CDC_TOP_GAIN_UPDATE__POR,
+ [TABLA_A_CDC_DEBUG_B1_CTL] = TABLA_A_CDC_DEBUG_B1_CTL__POR,
+ [TABLA_A_CDC_DEBUG_B2_CTL] = TABLA_A_CDC_DEBUG_B2_CTL__POR,
+ [TABLA_A_CDC_DEBUG_B3_CTL] = TABLA_A_CDC_DEBUG_B3_CTL__POR,
+ [TABLA_A_CDC_DEBUG_B4_CTL] = TABLA_A_CDC_DEBUG_B4_CTL__POR,
+ [TABLA_A_CDC_DEBUG_B5_CTL] = TABLA_A_CDC_DEBUG_B5_CTL__POR,
+ [TABLA_A_CDC_DEBUG_B6_CTL] = TABLA_A_CDC_DEBUG_B6_CTL__POR,
+ [TABLA_A_CDC_CONN_RX1_B1_CTL] = TABLA_A_CDC_CONN_RX1_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_RX1_B2_CTL] = TABLA_A_CDC_CONN_RX1_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_RX1_B3_CTL] = TABLA_A_CDC_CONN_RX1_B3_CTL__POR,
+ [TABLA_A_CDC_CONN_RX2_B1_CTL] = TABLA_A_CDC_CONN_RX2_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_RX2_B2_CTL] = TABLA_A_CDC_CONN_RX2_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_RX2_B3_CTL] = TABLA_A_CDC_CONN_RX2_B3_CTL__POR,
+ [TABLA_A_CDC_CONN_RX3_B1_CTL] = TABLA_A_CDC_CONN_RX3_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_RX3_B2_CTL] = TABLA_A_CDC_CONN_RX3_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_RX3_B3_CTL] = TABLA_A_CDC_CONN_RX3_B3_CTL__POR,
+ [TABLA_A_CDC_CONN_RX4_B1_CTL] = TABLA_A_CDC_CONN_RX4_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_RX4_B2_CTL] = TABLA_A_CDC_CONN_RX4_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_RX5_B1_CTL] = TABLA_A_CDC_CONN_RX5_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_RX5_B2_CTL] = TABLA_A_CDC_CONN_RX5_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_RX6_B1_CTL] = TABLA_A_CDC_CONN_RX6_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_RX6_B2_CTL] = TABLA_A_CDC_CONN_RX6_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_RX7_B1_CTL] = TABLA_A_CDC_CONN_RX7_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_RX7_B2_CTL] = TABLA_A_CDC_CONN_RX7_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_B1_CTL] = TABLA_A_CDC_CONN_TX_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_B2_CTL] = TABLA_A_CDC_CONN_TX_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_B3_CTL] = TABLA_A_CDC_CONN_TX_B3_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_B4_CTL] = TABLA_A_CDC_CONN_TX_B4_CTL__POR,
+ [TABLA_A_CDC_CONN_EQ1_B1_CTL] = TABLA_A_CDC_CONN_EQ1_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_EQ1_B2_CTL] = TABLA_A_CDC_CONN_EQ1_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_EQ1_B3_CTL] = TABLA_A_CDC_CONN_EQ1_B3_CTL__POR,
+ [TABLA_A_CDC_CONN_EQ1_B4_CTL] = TABLA_A_CDC_CONN_EQ1_B4_CTL__POR,
+ [TABLA_A_CDC_CONN_EQ2_B1_CTL] = TABLA_A_CDC_CONN_EQ2_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_EQ2_B2_CTL] = TABLA_A_CDC_CONN_EQ2_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_EQ2_B3_CTL] = TABLA_A_CDC_CONN_EQ2_B3_CTL__POR,
+ [TABLA_A_CDC_CONN_EQ2_B4_CTL] = TABLA_A_CDC_CONN_EQ2_B4_CTL__POR,
+ [TABLA_A_CDC_CONN_SRC1_B1_CTL] = TABLA_A_CDC_CONN_SRC1_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_SRC1_B2_CTL] = TABLA_A_CDC_CONN_SRC1_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_SRC2_B1_CTL] = TABLA_A_CDC_CONN_SRC2_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_SRC2_B2_CTL] = TABLA_A_CDC_CONN_SRC2_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B1_CTL] = TABLA_A_CDC_CONN_TX_SB_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B2_CTL] = TABLA_A_CDC_CONN_TX_SB_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B3_CTL] = TABLA_A_CDC_CONN_TX_SB_B3_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B4_CTL] = TABLA_A_CDC_CONN_TX_SB_B4_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B5_CTL] = TABLA_A_CDC_CONN_TX_SB_B5_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B6_CTL] = TABLA_A_CDC_CONN_TX_SB_B6_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B7_CTL] = TABLA_A_CDC_CONN_TX_SB_B7_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B8_CTL] = TABLA_A_CDC_CONN_TX_SB_B8_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B9_CTL] = TABLA_A_CDC_CONN_TX_SB_B9_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B10_CTL] = TABLA_A_CDC_CONN_TX_SB_B10_CTL__POR,
+ [TABLA_A_CDC_CONN_TX_SB_B11_CTL] = TABLA_A_CDC_CONN_TX_SB_B11_CTL__POR,
+ [TABLA_A_CDC_CONN_RX_SB_B1_CTL] = TABLA_A_CDC_CONN_RX_SB_B1_CTL__POR,
+ [TABLA_A_CDC_CONN_RX_SB_B2_CTL] = TABLA_A_CDC_CONN_RX_SB_B2_CTL__POR,
+ [TABLA_A_CDC_CONN_CLSG_CTL] = TABLA_A_CDC_CONN_CLSG_CTL__POR,
+ [TABLA_A_CDC_CONN_SPARE] = TABLA_A_CDC_CONN_SPARE__POR,
+ [TABLA_A_CDC_MBHC_EN_CTL] = TABLA_A_CDC_MBHC_EN_CTL__POR,
+ [TABLA_A_CDC_MBHC_FEATURE_B1_CFG] =
+ TABLA_A_CDC_MBHC_FEATURE_B1_CFG__POR,
+ [TABLA_A_CDC_MBHC_FEATURE_B2_CFG] =
+ TABLA_A_CDC_MBHC_FEATURE_B2_CFG__POR,
+ [TABLA_A_CDC_MBHC_TIMER_B1_CTL] = TABLA_A_CDC_MBHC_TIMER_B1_CTL__POR,
+ [TABLA_A_CDC_MBHC_TIMER_B2_CTL] = TABLA_A_CDC_MBHC_TIMER_B2_CTL__POR,
+ [TABLA_A_CDC_MBHC_TIMER_B3_CTL] = TABLA_A_CDC_MBHC_TIMER_B3_CTL__POR,
+ [TABLA_A_CDC_MBHC_TIMER_B4_CTL] = TABLA_A_CDC_MBHC_TIMER_B4_CTL__POR,
+ [TABLA_A_CDC_MBHC_TIMER_B5_CTL] = TABLA_A_CDC_MBHC_TIMER_B5_CTL__POR,
+ [TABLA_A_CDC_MBHC_TIMER_B6_CTL] = TABLA_A_CDC_MBHC_TIMER_B6_CTL__POR,
+ [TABLA_A_CDC_MBHC_B1_STATUS] = TABLA_A_CDC_MBHC_B1_STATUS__POR,
+ [TABLA_A_CDC_MBHC_B2_STATUS] = TABLA_A_CDC_MBHC_B2_STATUS__POR,
+ [TABLA_A_CDC_MBHC_B3_STATUS] = TABLA_A_CDC_MBHC_B3_STATUS__POR,
+ [TABLA_A_CDC_MBHC_B4_STATUS] = TABLA_A_CDC_MBHC_B4_STATUS__POR,
+ [TABLA_A_CDC_MBHC_B5_STATUS] = TABLA_A_CDC_MBHC_B5_STATUS__POR,
+ [TABLA_A_CDC_MBHC_B1_CTL] = TABLA_A_CDC_MBHC_B1_CTL__POR,
+ [TABLA_A_CDC_MBHC_B2_CTL] = TABLA_A_CDC_MBHC_B2_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B1_CTL] = TABLA_A_CDC_MBHC_VOLT_B1_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B2_CTL] = TABLA_A_CDC_MBHC_VOLT_B2_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B3_CTL] = TABLA_A_CDC_MBHC_VOLT_B3_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B4_CTL] = TABLA_A_CDC_MBHC_VOLT_B4_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B5_CTL] = TABLA_A_CDC_MBHC_VOLT_B5_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B6_CTL] = TABLA_A_CDC_MBHC_VOLT_B6_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B7_CTL] = TABLA_A_CDC_MBHC_VOLT_B7_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B8_CTL] = TABLA_A_CDC_MBHC_VOLT_B8_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B9_CTL] = TABLA_A_CDC_MBHC_VOLT_B9_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B10_CTL] = TABLA_A_CDC_MBHC_VOLT_B10_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B11_CTL] = TABLA_A_CDC_MBHC_VOLT_B11_CTL__POR,
+ [TABLA_A_CDC_MBHC_VOLT_B12_CTL] = TABLA_A_CDC_MBHC_VOLT_B12_CTL__POR,
+ [TABLA_A_CDC_MBHC_CLK_CTL] = TABLA_A_CDC_MBHC_CLK_CTL__POR,
+ [TABLA_A_CDC_MBHC_INT_CTL] = TABLA_A_CDC_MBHC_INT_CTL__POR,
+ [TABLA_A_CDC_MBHC_DEBUG_CTL] = TABLA_A_CDC_MBHC_DEBUG_CTL__POR,
+ [TABLA_A_CDC_MBHC_SPARE] = TABLA_A_CDC_MBHC_SPARE__POR,
+};
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
new file mode 100644
index 0000000..de1b199
--- /dev/null
+++ b/sound/soc/codecs/wcd9310.c
@@ -0,0 +1,1598 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/mfd/wcd9310/core.h>
+#include <linux/mfd/wcd9310/registers.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include "wcd9310.h"
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+enum tabla_bandgap_type {
+ TABLA_BANDGAP_OFF = 0,
+ TABLA_BANDGAP_AUDIO_MODE,
+ TABLA_BANDGAP_MBHC_MODE,
+};
+
+struct tabla_priv { /* member undecided */
+ struct snd_soc_codec *codec;
+ u32 ref_cnt;
+ u32 adc_count;
+ u32 dec_count;
+ enum tabla_bandgap_type bandgap_type;
+ bool clock_active;
+ bool config_mode_active;
+ bool mbhc_polling_active;
+
+ struct tabla_mbhc_calibration *calibration;
+
+ struct snd_soc_jack *jack;
+};
+
+static int tabla_codec_enable_charge_pump(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if ((tabla->bandgap_type != TABLA_BANDGAP_AUDIO_MODE) ||
+ (!tabla->clock_active)) {
+ pr_err("%s: Error, Tabla must have clocks enabled for "
+ "charge pump\n", __func__);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, TABLA_A_CP_EN, 0x01, 0x01);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_CTL, 0x01,
+ 0x01);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLSG_CTL, 0x08, 0x08);
+ usleep_range(200, 200);
+ snd_soc_update_bits(codec, TABLA_A_CP_STATIC, 0x10, 0x00);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_RESET_CTL, 0x10,
+ 0x10);
+ usleep_range(20, 20);
+ snd_soc_update_bits(codec, TABLA_A_CP_STATIC, 0x08, 0x08);
+ snd_soc_update_bits(codec, TABLA_A_CP_STATIC, 0x10, 0x10);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLSG_CTL, 0x08, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_CTL, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec, TABLA_A_CP_STATIC, 0x08, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_CP_EN, 0x01, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_kcontrol_new tabla_snd_controls[] = {
+ SOC_SINGLE_TLV("LINEOUT1 Volume", TABLA_A_RX_LINE_1_GAIN, 0, 12, 1,
+ line_gain),
+ SOC_SINGLE_TLV("LINEOUT3 Volume", TABLA_A_RX_LINE_3_GAIN, 0, 12, 1,
+ line_gain),
+ SOC_SINGLE_TLV("HPHL Volume", TABLA_A_RX_HPH_L_GAIN, 0, 12, 1,
+ line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", TABLA_A_RX_HPH_R_GAIN, 0, 12, 1,
+ line_gain),
+
+ SOC_SINGLE_TLV("RX1 Digital Volume", TABLA_A_CDC_RX1_VOL_CTL_B2_CTL, 0,
+ 100, 0, digital_gain),
+ SOC_SINGLE_TLV("RX2 Digital Volume", TABLA_A_CDC_RX2_VOL_CTL_B2_CTL, 0,
+ 100, 0, digital_gain),
+
+ SOC_SINGLE_TLV("DEC5 Volume", TABLA_A_CDC_TX5_VOL_CTL_GAIN, 0, 100, 0,
+ digital_gain),
+ SOC_SINGLE_TLV("DEC6 Volume", TABLA_A_CDC_TX6_VOL_CTL_GAIN, 0, 100, 0,
+ digital_gain),
+
+ SOC_SINGLE_TLV("ADC1 Volume", TABLA_A_TX_1_2_EN, 1, 3, 0, analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", TABLA_A_TX_1_2_EN, 5, 3, 0, analog_gain),
+
+ SOC_SINGLE("MICBIAS1 CAPLESS Switch", TABLA_A_MICB_1_CTL, 4, 1, 1),
+};
+
+static const char *rx_mix1_text[] = {
+ "ZERO", "SRC1", "SRC2", "IIR1", "IIR2", "RX1", "RX2", "RX3", "RX4",
+ "RX5", "RX6", "RX7"
+};
+
+static const char *sb_tx1_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC1"
+};
+
+static const char *sb_tx5_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC5"
+};
+
+static const char *sb_tx6_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC6"
+};
+
+static const char const *sb_tx7_to_tx10_mux_text[] = {
+ "ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+ "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+ "DEC9", "DEC10"
+};
+
+static const char *dec1_mux_text[] = {
+ "ZERO", "DMIC1", "ADC6",
+};
+
+static const char *dec5_mux_text[] = {
+ "ZERO", "DMIC5", "ADC2",
+};
+
+static const char *dec6_mux_text[] = {
+ "ZERO", "DMIC6", "ADC1",
+};
+
+static const char const *dec7_mux_text[] = {
+ "ZERO", "DMIC1", "DMIC6", "ADC1", "ADC6", "ANC1_FB", "ANC2_FB",
+};
+
+static const char *iir1_inp1_text[] = {
+ "ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+ "DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const struct soc_enum rx_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX1_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx2_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX2_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx3_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX3_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx4_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX4_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx5_mix1_inp1_chain_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_RX5_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum sb_tx5_mux_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_TX_SB_B5_CTL, 0, 9, sb_tx5_mux_text);
+
+static const struct soc_enum sb_tx6_mux_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_TX_SB_B6_CTL, 0, 9, sb_tx6_mux_text);
+
+static const struct soc_enum sb_tx7_mux_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_TX_SB_B7_CTL, 0, 18,
+ sb_tx7_to_tx10_mux_text);
+
+static const struct soc_enum sb_tx8_mux_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_TX_SB_B8_CTL, 0, 18,
+ sb_tx7_to_tx10_mux_text);
+
+static const struct soc_enum sb_tx1_mux_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_TX_SB_B1_CTL, 0, 9, sb_tx1_mux_text);
+
+static const struct soc_enum dec1_mux_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_TX_B1_CTL, 0, 3, dec1_mux_text);
+
+static const struct soc_enum dec5_mux_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_TX_B2_CTL, 0, 3, dec5_mux_text);
+
+static const struct soc_enum dec6_mux_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_TX_B2_CTL, 2, 3, dec6_mux_text);
+
+static const struct soc_enum dec7_mux_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_TX_B2_CTL, 4, 7, dec7_mux_text);
+
+static const struct soc_enum iir1_inp1_mux_enum =
+ SOC_ENUM_SINGLE(TABLA_A_CDC_CONN_EQ1_B1_CTL, 0, 18, iir1_inp1_text);
+
+static const struct snd_kcontrol_new rx_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx3_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx4_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX4 MIX1 INP1 Mux", rx4_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx5_mix1_inp1_mux =
+ SOC_DAPM_ENUM("RX5 MIX1 INP1 Mux", rx5_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new sb_tx5_mux =
+ SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx6_mux =
+ SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx7_mux =
+ SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx8_mux =
+ SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx1_mux =
+ SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum);
+
+static const struct snd_kcontrol_new dec1_mux =
+ SOC_DAPM_ENUM("DEC1 MUX Mux", dec1_mux_enum);
+
+static const struct snd_kcontrol_new dec5_mux =
+ SOC_DAPM_ENUM("DEC5 MUX Mux", dec5_mux_enum);
+
+static const struct snd_kcontrol_new dec6_mux =
+ SOC_DAPM_ENUM("DEC6 MUX Mux", dec6_mux_enum);
+
+static const struct snd_kcontrol_new dec7_mux =
+ SOC_DAPM_ENUM("DEC7 MUX Mux", dec7_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp1_mux =
+ SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+
+static const struct snd_kcontrol_new dac1_control =
+ SOC_DAPM_SINGLE("Switch", TABLA_A_RX_EAR_EN, 5, 1, 0);
+
+static const struct snd_kcontrol_new hphl_switch =
+ SOC_DAPM_SINGLE("Switch", TABLA_A_RX_HPH_L_DAC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new hphr_switch =
+ SOC_DAPM_SINGLE("Switch", TABLA_A_RX_HPH_R_DAC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new lineout1_switch =
+ SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_1_DAC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new lineout3_switch =
+ SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_3_DAC_CTL, 6, 1, 0);
+
+static void tabla_codec_enable_adc_block(struct snd_soc_codec *codec,
+ int enable)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s %d\n", __func__, enable);
+
+ if (enable) {
+ tabla->adc_count++;
+ snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS, 0xE0, 0xE0);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_CTL, 0x2, 0x2);
+ } else {
+ tabla->adc_count--;
+ if (!tabla->adc_count) {
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_CTL,
+ 0x2, 0x0);
+ if (!tabla->mbhc_polling_active)
+ snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS,
+ 0xE0, 0x0);
+ }
+ }
+}
+
+static int tabla_codec_enable_adc(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u16 adc_reg;
+
+ pr_debug("%s %d\n", __func__, event);
+
+ if (w->reg == TABLA_A_TX_1_2_EN)
+ adc_reg = TABLA_A_TX_1_2_TEST_CTL;
+ else if (w->reg == TABLA_A_TX_3_4_EN)
+ adc_reg = TABLA_A_TX_3_4_TEST_CTL;
+ else if (w->reg == TABLA_A_TX_5_6_EN)
+ adc_reg = TABLA_A_TX_5_6_TEST_CTL;
+ else {
+ pr_err("%s: Error, invalid adc register\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tabla_codec_enable_adc_block(codec, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, adc_reg, 1 << w->shift,
+ 1 << w->shift);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, adc_reg, 1 << w->shift, 0x00);
+ usleep_range(1000, 1000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tabla_codec_enable_adc_block(codec, 0);
+ break;
+ }
+ return 0;
+}
+
+static int tabla_codec_enable_pamp_gain(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ pr_debug("%s %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, TABLA_A_RX_EAR_GAIN, 0x80, 0x80);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, TABLA_A_RX_EAR_GAIN, 0x80, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int tabla_codec_enable_lineout(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u16 lineout_gain_reg;
+
+ pr_debug("%s %d\n", __func__, event);
+
+ switch (w->shift) {
+ case 0:
+ lineout_gain_reg = TABLA_A_RX_LINE_1_GAIN;
+ break;
+ case 1:
+ lineout_gain_reg = TABLA_A_RX_LINE_2_GAIN;
+ break;
+ case 2:
+ lineout_gain_reg = TABLA_A_RX_LINE_3_GAIN;
+ break;
+ case 3:
+ lineout_gain_reg = TABLA_A_RX_LINE_4_GAIN;
+ break;
+ case 4:
+ lineout_gain_reg = TABLA_A_RX_LINE_5_GAIN;
+ break;
+ default:
+ pr_err("%s: Error, incorrect lineout register value\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x40);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(40000, 40000);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x00);
+ break;
+ }
+ return 0;
+}
+
+static int tabla_codec_enable_dmic1(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ pr_debug("%s %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, TABLA_A_CDC_TX1_MUX_CTL, 0x1, 0x1);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_DMIC_CTL, 0x2, 0x2);
+ snd_soc_update_bits(codec, TABLA_A_CDC_TX1_DMIC_CTL, 0x1, 0x1);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_DMIC_CTL, 0x1, 0x1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, TABLA_A_CDC_TX1_DMIC_CTL, 0x1, 0);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_DMIC_CTL, 0x3, 0);
+ break;
+ }
+ return 0;
+}
+
+static int tabla_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u16 micb_cfilt_reg, micb_int_reg;
+ char *internal_text = "Internal";
+
+ pr_debug("%s %d\n", __func__, event);
+ switch (w->reg) {
+ case TABLA_A_MICB_1_CTL:
+ micb_cfilt_reg = TABLA_A_MICB_CFILT_1_CTL;
+ micb_int_reg = TABLA_A_MICB_1_INT_RBIAS;
+ break;
+ case TABLA_A_MICB_2_CTL:
+ micb_cfilt_reg = TABLA_A_MICB_CFILT_2_CTL;
+ micb_int_reg = TABLA_A_MICB_2_INT_RBIAS;
+ break;
+ case TABLA_A_MICB_3_CTL:
+ micb_cfilt_reg = TABLA_A_MICB_CFILT_3_CTL;
+ micb_int_reg = TABLA_A_MICB_3_INT_RBIAS;
+ break;
+ default:
+ pr_err("%s: Error, invalid micbias register\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
+ if (strnstr(w->name, internal_text, 20))
+ snd_soc_update_bits(codec, micb_int_reg, 0xE0, 0xE0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (strnstr(w->name, internal_text, 20))
+ snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
+ snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static void tabla_codec_enable_dec_clock(struct snd_soc_codec *codec,
+ int enable)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ if (enable) {
+ tabla->dec_count++;
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_CTL, 0x4, 0x4);
+ } else {
+ tabla->dec_count--;
+ if (!tabla->dec_count)
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_CTL,
+ 0x4, 0x0);
+ }
+}
+
+static int tabla_codec_enable_dec(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u16 dec_reset_reg;
+
+ pr_debug("%s %d\n", __func__, event);
+
+ if (w->reg == TABLA_A_CDC_CLK_TX_CLK_EN_B1_CTL)
+ dec_reset_reg = TABLA_A_CDC_CLK_TX_RESET_B1_CTL;
+ else if (w->reg == TABLA_A_CDC_CLK_TX_CLK_EN_B2_CTL)
+ dec_reset_reg = TABLA_A_CDC_CLK_TX_RESET_B2_CTL;
+ else {
+ pr_err("%s: Error, incorrect dec\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ tabla_codec_enable_dec_clock(codec, 1);
+ snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift,
+ 1 << w->shift);
+ snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ tabla_codec_enable_dec_clock(codec, 0);
+ break;
+ }
+ return 0;
+}
+
+static int tabla_codec_reset_interpolator_1(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x1,
+ 0x1);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x1,
+ 0x0);
+ break;
+ }
+ return 0;
+}
+
+static int tabla_codec_reset_interpolator_2(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x2,
+ 0x2);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x2,
+ 0x0);
+ break;
+ }
+ return 0;
+}
+
+static int tabla_codec_reset_interpolator_3(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x4,
+ 0x4);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x4,
+ 0x0);
+ break;
+ }
+ return 0;
+}
+
+static int tabla_codec_reset_interpolator_4(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x8,
+ 0x8);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x8,
+ 0x0);
+ break;
+ }
+ return 0;
+}
+
+static int tabla_codec_reset_interpolator_5(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x10,
+ 0x10);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_RX_RESET_CTL, 0x10,
+ 0x0);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tabla_dapm_widgets[] = {
+ /*RX stuff */
+ SND_SOC_DAPM_OUTPUT("EAR"),
+
+ SND_SOC_DAPM_PGA_E("EAR PA", TABLA_A_RX_EAR_EN, 4, 0, NULL, 0,
+ tabla_codec_enable_pamp_gain, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA("EAR PA Input", TABLA_A_CDC_CLSG_CTL, 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("DAC1", TABLA_A_RX_EAR_EN, 6, 0, &dac1_control),
+ SND_SOC_DAPM_PGA_E("RX1 CP", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tabla_codec_enable_charge_pump, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA("RX BIAS", TABLA_A_RX_COM_BIAS, 7, 0, NULL, 0),
+ SND_SOC_DAPM_MUX_E("RX1 MIX1 INP1", TABLA_A_CDC_CLK_RX_B1_CTL, 0, 0,
+ &rx_mix1_inp1_mux, tabla_codec_reset_interpolator_1,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_AIF_IN("SLIM RX1", "AIF1 Playback", 0,
+ TABLA_A_CDC_RX1_B6_CTL, 5, 0),
+
+ /* RX 2 path */
+ SND_SOC_DAPM_PGA_E("RX2 CP", SND_SOC_NOPM, 0, 0, NULL, 0,
+ tabla_codec_enable_charge_pump, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("RX2 MIX1 INP1", TABLA_A_CDC_CLK_RX_B1_CTL, 1, 0,
+ &rx2_mix1_inp1_mux, tabla_codec_reset_interpolator_2,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_AIF_IN("SLIM RX2", "AIF1 Playback", 0,
+ TABLA_A_CDC_RX2_B6_CTL, 5, 0),
+
+ /* Headphone */
+ SND_SOC_DAPM_OUTPUT("HEADPHONE"),
+ SND_SOC_DAPM_PGA("HPHL", TABLA_A_RX_HPH_CNP_EN, 5, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("HPHL DAC", TABLA_A_RX_HPH_L_DAC_CTL, 7, 0,
+ &hphl_switch),
+
+ SND_SOC_DAPM_PGA("HPHR", TABLA_A_RX_HPH_CNP_EN, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("HPHR DAC", TABLA_A_RX_HPH_R_DAC_CTL, 7, 0,
+ &hphr_switch),
+
+ /* Speaker */
+ SND_SOC_DAPM_OUTPUT("LINEOUT"),
+ SND_SOC_DAPM_PGA_E("LINEOUT1", TABLA_A_RX_LINE_CNP_EN, 0, 0, NULL, 0,
+ tabla_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("LINEOUT1 DAC", TABLA_A_RX_LINE_1_DAC_CTL, 7, 0,
+ &lineout1_switch),
+ SND_SOC_DAPM_MUX_E("RX3 MIX1 INP1", TABLA_A_CDC_CLK_RX_B1_CTL, 2, 0,
+ &rx3_mix1_inp1_mux, tabla_codec_reset_interpolator_3,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_PGA_E("LINEOUT3", TABLA_A_RX_LINE_CNP_EN, 2, 0, NULL, 0,
+ tabla_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SWITCH("LINEOUT3 DAC", TABLA_A_RX_LINE_3_DAC_CTL, 7, 0,
+ &lineout3_switch),
+ SND_SOC_DAPM_MUX_E("RX4 MIX1 INP1", TABLA_A_CDC_CLK_RX_B1_CTL, 3, 0,
+ &rx4_mix1_inp1_mux, tabla_codec_reset_interpolator_4,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX_E("RX5 MIX1 INP1", TABLA_A_CDC_CLK_RX_B1_CTL, 4, 0,
+ &rx5_mix1_inp1_mux, tabla_codec_reset_interpolator_5,
+ SND_SOC_DAPM_PRE_PMU),
+
+ /* TX */
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 External", TABLA_A_MICB_1_CTL, 7, 0,
+ tabla_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 Internal", TABLA_A_MICB_1_CTL, 7, 0,
+ tabla_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, TABLA_A_TX_1_2_EN, 7, 0,
+ tabla_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC1 MUX", TABLA_A_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0,
+ &dec1_mux, tabla_codec_enable_dec, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC5 MUX", TABLA_A_CDC_CLK_TX_CLK_EN_B1_CTL, 4, 0,
+ &dec5_mux, tabla_codec_enable_dec, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC6 MUX", TABLA_A_CDC_CLK_TX_CLK_EN_B1_CTL, 5, 0,
+ &dec6_mux, tabla_codec_enable_dec, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX_E("DEC7 MUX", TABLA_A_CDC_CLK_TX_CLK_EN_B1_CTL, 6, 0,
+ &dec7_mux, tabla_codec_enable_dec, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 External", TABLA_A_MICB_2_CTL, 7, 0,
+ tabla_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS2 Internal", TABLA_A_MICB_2_CTL, 7, 0,
+ tabla_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 External", TABLA_A_MICB_3_CTL, 7, 0,
+ tabla_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MICBIAS_E("MIC BIAS3 Internal", TABLA_A_MICB_3_CTL, 7, 0,
+ tabla_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC2", NULL, TABLA_A_TX_1_2_EN, 3, 0,
+ tabla_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, 0, 0, &sb_tx1_mux),
+ SND_SOC_DAPM_AIF_OUT("SLIM TX1", "AIF1 Capture", NULL, SND_SOC_NOPM,
+ 0, 0),
+
+ SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, 0, 0, &sb_tx5_mux),
+ SND_SOC_DAPM_AIF_OUT("SLIM TX5", "AIF1 Capture", NULL, SND_SOC_NOPM,
+ 4, 0),
+
+ SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, 0, 0, &sb_tx6_mux),
+ SND_SOC_DAPM_AIF_OUT("SLIM TX6", "AIF1 Capture", NULL, SND_SOC_NOPM,
+ 5, 0),
+
+ SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, 0, 0, &sb_tx7_mux),
+ SND_SOC_DAPM_AIF_OUT("SLIM TX7", "AIF1 Capture", NULL, SND_SOC_NOPM,
+ 0, 0),
+
+ SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, 0, 0, &sb_tx8_mux),
+ SND_SOC_DAPM_AIF_OUT("SLIM TX8", "AIF1 Capture", NULL, SND_SOC_NOPM,
+ 0, 0),
+
+ /* Digital Mic */
+ SND_SOC_DAPM_INPUT("DMIC1 IN"),
+ SND_SOC_DAPM_MIC("DMIC1", &tabla_codec_enable_dmic1),
+
+ /* Sidetone */
+ SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+ SND_SOC_DAPM_PGA("IIR1", TABLA_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* SLIMBUS Connections */
+ {"RX BIAS", NULL, "SLIM RX1"},
+ {"RX BIAS", NULL, "SLIM RX2"},
+
+ {"SLIM TX1", NULL, "SLIM TX1 MUX"},
+ {"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
+
+ {"SLIM TX5", NULL, "SLIM TX5 MUX"},
+ {"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
+
+ {"SLIM TX6", NULL, "SLIM TX6 MUX"},
+ {"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
+
+ {"SLIM TX7", NULL, "SLIM TX7 MUX"},
+ {"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
+ {"SLIM TX7 MUX", "DEC7", "DEC7 MUX"},
+ {"SLIM TX7 MUX", "DEC5", "DEC5 MUX"},
+ {"SLIM TX7 MUX", "DEC6", "DEC6 MUX"},
+
+ {"SLIM TX8", NULL, "SLIM TX8 MUX"},
+ {"SLIM TX8 MUX", "DEC5", "DEC5 MUX"},
+ {"SLIM TX8 MUX", "DEC6", "DEC6 MUX"},
+
+ /* Earpiece (RX MIX1) */
+ {"EAR", NULL, "EAR PA"},
+ {"EAR PA", NULL, "EAR PA Input"},
+ {"EAR PA Input", NULL, "DAC1"},
+ {"DAC1", "Switch", "RX1 CP"},
+ {"RX1 CP", NULL, "RX1 MIX1 INP1"},
+ {"RX1 MIX1 INP1", "RX1", "RX BIAS"},
+
+ /* Headset (RX MIX1 and RX MIX2) */
+ {"HEADPHONE", NULL, "HPHL"},
+ {"HPHL", NULL, "HPHL DAC"},
+ {"HPHL DAC", "Switch", "RX1 MIX1 INP1"},
+
+ {"HEADPHONE", NULL, "HPHR"},
+ {"HPHR", NULL, "HPHR DAC"},
+ {"HPHR DAC", "Switch", "RX2 CP"},
+ {"RX2 CP", NULL, "RX2 MIX1 INP1"},
+ {"RX2 MIX1 INP1", "RX2", "RX BIAS"},
+
+ /* Speaker (RX MIX3 and RX MIX4) */
+ {"LINEOUT", NULL, "LINEOUT1"},
+ {"LINEOUT1", NULL, "LINEOUT1 DAC"},
+ {"LINEOUT1 DAC", "Switch", "RX3 MIX1 INP1"},
+ {"RX3 MIX1 INP1", "RX1", "RX BIAS"},
+
+ {"LINEOUT", NULL, "LINEOUT3"},
+ {"LINEOUT3", NULL, "LINEOUT3 DAC"},
+ {"LINEOUT3 DAC", "Switch", "RX5 MIX1 INP1"},
+ {"RX4 MIX1 INP1", "RX2", "RX BIAS"},
+ {"RX5 MIX1 INP1", "RX2", "RX BIAS"},
+
+ /* Handset TX */
+ {"DEC5 MUX", "ADC2", "ADC2"},
+ {"DEC6 MUX", "ADC1", "ADC1"},
+ {"ADC1", NULL, "AMIC1"},
+ {"ADC2", NULL, "AMIC2"},
+
+ /* Digital Mic */
+ {"DEC1 MUX", "DMIC1", "DMIC1"},
+ {"DEC7 MUX", "DMIC1", "DMIC1"},
+ {"DMIC1", NULL, "DMIC1 IN"},
+
+ /* Sidetone (IIR1) */
+ {"RX1 MIX1 INP1", "IIR1", "IIR1"},
+ {"IIR1", NULL, "IIR1 INP1 MUX"},
+ {"IIR1 INP1 MUX", "DEC6", "DEC6 MUX"},
+
+};
+
+static int tabla_readable(struct snd_soc_codec *ssc, unsigned int reg)
+{
+ return tabla_reg_readable[reg];
+}
+
+static int tabla_volatile(struct snd_soc_codec *ssc, unsigned int reg)
+{
+ /* Registers lower than 0x100 are top level registers which can be
+ * written by the Tabla core driver.
+ */
+
+ if ((reg >= TABLA_A_CDC_MBHC_EN_CTL) || (reg < 0x100))
+ return 1;
+
+ return 0;
+}
+
+#define TABLA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+static int tabla_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ int ret;
+ pr_debug("%s: write reg %x val %x\n", __func__, reg, value);
+
+ BUG_ON(reg > TABLA_MAX_REGISTER);
+
+ if (!tabla_volatile(codec, reg)) {
+ pr_debug("writing to cache\n");
+ ret = snd_soc_cache_write(codec, reg, value);
+ if (ret != 0)
+ dev_err(codec->dev, "Cache write to %x failed: %d\n",
+ reg, ret);
+ }
+
+ return tabla_reg_write(codec->control_data, reg, value);
+}
+static unsigned int tabla_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ unsigned int val;
+ int ret;
+
+ BUG_ON(reg > TABLA_MAX_REGISTER);
+
+ if (!tabla_volatile(codec, reg) && tabla_readable(codec, reg) &&
+ reg < codec->driver->reg_cache_size) {
+ pr_debug("reading from cache\n");
+ ret = snd_soc_cache_read(codec, reg, &val);
+ if (ret >= 0) {
+ pr_debug("register %d, value %d\n", reg, val);
+ return val;
+ } else
+ dev_err(codec->dev, "Cache read from %x failed: %d\n",
+ reg, ret);
+ }
+
+ val = tabla_reg_read(codec->control_data, reg);
+ pr_debug("%s: read reg %x val %x\n", __func__, reg, val);
+ return val;
+}
+
+static void tabla_codec_enable_audio_mode_bandgap(struct snd_soc_codec *codec)
+{
+ snd_soc_write(codec, TABLA_A_BIAS_REF_CTL, 0x1C);
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x80,
+ 0x80);
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x04,
+ 0x04);
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x01,
+ 0x01);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x80,
+ 0x00);
+}
+
+static void tabla_codec_enable_bandgap(struct snd_soc_codec *codec,
+ enum tabla_bandgap_type choice)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ /* TODO lock resources accessed by audio streams and threaded
+ * interrupt handlers
+ */
+
+ pr_debug("%s, choice is %d, current is %d\n", __func__, choice,
+ tabla->bandgap_type);
+
+ if (tabla->bandgap_type == choice)
+ return;
+
+ if ((tabla->bandgap_type == TABLA_BANDGAP_OFF) &&
+ (choice == TABLA_BANDGAP_AUDIO_MODE)) {
+ tabla_codec_enable_audio_mode_bandgap(codec);
+ } else if ((tabla->bandgap_type == TABLA_BANDGAP_AUDIO_MODE) &&
+ (choice == TABLA_BANDGAP_MBHC_MODE)) {
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x2,
+ 0x2);
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x80,
+ 0x80);
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x4,
+ 0x4);
+ usleep_range(1000, 1000);
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x80,
+ 0x00);
+ } else if ((tabla->bandgap_type == TABLA_BANDGAP_MBHC_MODE) &&
+ (choice == TABLA_BANDGAP_AUDIO_MODE)) {
+ snd_soc_write(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x00);
+ usleep_range(100, 100);
+ tabla_codec_enable_audio_mode_bandgap(codec);
+ } else if (choice == TABLA_BANDGAP_OFF) {
+ snd_soc_write(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x00);
+ } else {
+ pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+ }
+ tabla->bandgap_type = choice;
+}
+
+static int tabla_codec_enable_config_mode(struct snd_soc_codec *codec,
+ int enable)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ if (enable) {
+ snd_soc_update_bits(codec, TABLA_A_CONFIG_MODE_FREQ, 0x10, 0);
+ snd_soc_write(codec, TABLA_A_BIAS_CONFIG_MODE_BG_CTL, 0x17);
+ usleep_range(5, 5);
+ snd_soc_update_bits(codec, TABLA_A_CONFIG_MODE_FREQ, 0x80,
+ 0x80);
+ snd_soc_update_bits(codec, TABLA_A_CONFIG_MODE_TEST, 0x80,
+ 0x80);
+ usleep_range(10, 10);
+ snd_soc_update_bits(codec, TABLA_A_CONFIG_MODE_TEST, 0x80, 0);
+ usleep_range(20, 20);
+ snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN1, 0x08, 0x08);
+ } else {
+ snd_soc_update_bits(codec, TABLA_A_BIAS_CONFIG_MODE_BG_CTL, 0x1,
+ 0);
+ snd_soc_update_bits(codec, TABLA_A_CONFIG_MODE_FREQ, 0x80, 0);
+ }
+ tabla->config_mode_active = enable ? true : false;
+
+ return 0;
+}
+
+static int tabla_codec_enable_clock_block(struct snd_soc_codec *codec,
+ int config_mode)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s\n", __func__);
+
+ if (config_mode) {
+ tabla_codec_enable_config_mode(codec, 1);
+ snd_soc_write(codec, TABLA_A_CLK_BUFF_EN2, 0x00);
+ snd_soc_write(codec, TABLA_A_CLK_BUFF_EN2, 0x02);
+ snd_soc_write(codec, TABLA_A_CLK_BUFF_EN1, 0x0D);
+ usleep_range(1000, 1000);
+ } else
+ snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN1, 0x08, 0x00);
+
+ if (!config_mode && tabla->mbhc_polling_active) {
+ snd_soc_write(codec, TABLA_A_CLK_BUFF_EN2, 0x02);
+ tabla_codec_enable_config_mode(codec, 0);
+
+ }
+
+ snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN1, 0x05, 0x05);
+ snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN2, 0x02, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN2, 0x04, 0x04);
+ snd_soc_update_bits(codec, TABLA_A_CDC_CLK_MCLK_CTL, 0x01, 0x01);
+ usleep_range(50, 50);
+ tabla->clock_active = true;
+ return 0;
+}
+static void tabla_codec_disable_clock_block(struct snd_soc_codec *codec)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ pr_debug("%s\n", __func__);
+ snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN2, 0x04, 0x00);
+ ndelay(160);
+ snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN2, 0x02, 0x02);
+ snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN1, 0x05, 0x00);
+ tabla->clock_active = false;
+}
+
+static int tabla_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ pr_debug("%s()\n", __func__);
+
+ if (!codec) {
+ pr_err("Error, no codec found\n");
+ return -EINVAL;
+ }
+ tabla->ref_cnt++;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ /* Enable LDO */
+ snd_soc_update_bits(codec, TABLA_A_MICB_CFILT_1_VAL, 0xFC,
+ 0xA0);
+ snd_soc_update_bits(codec, TABLA_A_LDO_H_MODE_1, 0x80, 0x80);
+ usleep_range(1000, 1000);
+ }
+
+ if (tabla->mbhc_polling_active && (tabla->ref_cnt == 1)) {
+ tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_AUDIO_MODE);
+ tabla_codec_enable_clock_block(codec, 0);
+ }
+
+ return ret;
+}
+
+static void tabla_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("%s()\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ /* Disable LDO */
+ snd_soc_update_bits(codec, TABLA_A_LDO_H_MODE_1, 0x80, 0x00);
+ usleep_range(1000, 1000);
+ }
+
+ if (!tabla->ref_cnt) {
+ pr_err("Error, trying to shutdown codec when already down\n");
+ return;
+ }
+ tabla->ref_cnt--;
+
+ if (tabla->mbhc_polling_active) {
+ if (!tabla->ref_cnt) {
+ tabla_codec_enable_bandgap(codec,
+ TABLA_BANDGAP_MBHC_MODE);
+ snd_soc_update_bits(codec, TABLA_A_RX_COM_BIAS, 0x80,
+ 0x80);
+ tabla_codec_enable_clock_block(codec, 1);
+ }
+ snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN1, 0x05, 0x01);
+ }
+}
+
+static int tabla_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ pr_debug("%s %d\n", __func__, mute);
+
+ /* TODO mute TX */
+ if (mute)
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX1_B6_CTL, 0x01, 0x01);
+ else
+ snd_soc_update_bits(codec, TABLA_A_CDC_RX1_B6_CTL, 0x01, 0x00);
+
+ return 0;
+}
+
+static int tabla_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int tabla_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int tabla_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s: DAI-ID %x\n", __func__, dai->id);
+ return 0;
+}
+
+static struct snd_soc_dai_ops tabla_dai_ops = {
+ .startup = tabla_startup,
+ .shutdown = tabla_shutdown,
+ .hw_params = tabla_hw_params,
+ .set_sysclk = tabla_set_dai_sysclk,
+ .set_fmt = tabla_set_dai_fmt,
+ .digital_mute = tabla_digital_mute,
+};
+
+static struct snd_soc_dai_driver tabla_dai[] = {
+ {
+ .name = "tabla_rx1",
+ .id = 1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = TABLA_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tabla_dai_ops,
+ },
+ {
+ .name = "tabla_tx1",
+ .id = 2,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = TABLA_FORMATS,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &tabla_dai_ops,
+ },
+};
+
+static void tabla_codec_setup_hs_polling(struct snd_soc_codec *codec)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ struct tabla_mbhc_calibration *calibration = tabla->calibration;
+ int micbias_ctl_reg, micbias_cfilt_val_reg, micbias_cfilt_ctl_reg;
+
+ if (!calibration) {
+ pr_err("Error, no tabla calibration\n");
+ return;
+ }
+
+ tabla->mbhc_polling_active = true;
+
+ if (!tabla->ref_cnt) {
+ tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_MBHC_MODE);
+ snd_soc_update_bits(codec, TABLA_A_RX_COM_BIAS, 0x80, 0x80);
+ tabla_codec_enable_clock_block(codec, 1);
+ }
+
+ snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN1, 0x05, 0x01);
+
+ /* TODO store register values in calibration */
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B4_CTL, 0x09);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B3_CTL, 0xEE);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B2_CTL, 0xFC);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B1_CTL, 0xCE);
+
+ snd_soc_update_bits(codec, TABLA_A_LDO_H_MODE_1, 0x0F, 0x0D);
+ snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS, 0xE0, 0xE0);
+
+ /* TODO select cfilt separately from the micbias line inside the machine
+ * driver
+ */
+ switch (calibration->bias) {
+ case TABLA_MICBIAS1:
+ micbias_ctl_reg = TABLA_A_MICB_1_CTL;
+ micbias_cfilt_ctl_reg = TABLA_A_MICB_CFILT_1_CTL;
+ micbias_cfilt_val_reg = TABLA_A_MICB_CFILT_1_VAL;
+ break;
+ case TABLA_MICBIAS2:
+ micbias_ctl_reg = TABLA_A_MICB_2_CTL;
+ micbias_cfilt_ctl_reg = TABLA_A_MICB_CFILT_2_CTL;
+ micbias_cfilt_val_reg = TABLA_A_MICB_CFILT_2_VAL;
+ break;
+ case TABLA_MICBIAS3:
+ micbias_ctl_reg = TABLA_A_MICB_3_CTL;
+ micbias_cfilt_ctl_reg = TABLA_A_MICB_CFILT_3_CTL;
+ micbias_cfilt_val_reg = TABLA_A_MICB_CFILT_3_VAL;
+ break;
+ case TABLA_MICBIAS4:
+ pr_err("%s: Error, microphone bias 4 not supported\n",
+ __func__);
+ return;
+ default:
+ pr_err("Error, invalid mic bias line\n");
+ return;
+ }
+ snd_soc_write(codec, micbias_cfilt_ctl_reg, 0x40);
+
+ snd_soc_write(codec, micbias_ctl_reg, 0x36);
+
+ snd_soc_write(codec, micbias_cfilt_val_reg, 0x68);
+
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+ snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x4);
+
+ snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_EN, 0x80, 0x80);
+ snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_EN, 0x1F, 0x1C);
+ snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
+
+ snd_soc_update_bits(codec, TABLA_A_TX_7_MBHC_EN, 0x80, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x80, 0x80);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x80, 0x00);
+
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL, 3);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B2_CTL, 9);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B3_CTL, 30);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, 120);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL, 0x78, 0x58);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_B2_CTL, 11);
+
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+
+ tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x1);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x1);
+ /* TODO check if we need to maintain the other register bits */
+}
+
+static int tabla_codec_enable_hs_detect(struct snd_soc_codec *codec,
+ int insertion)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ struct tabla_mbhc_calibration *calibration = tabla->calibration;
+ int central_bias_enabled = 0;
+ int micbias_int_reg, micbias_ctl_reg, micbias_mbhc_reg;
+
+ if (!calibration) {
+ pr_err("Error, no tabla calibration\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0);
+
+ if (insertion)
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x2, 0);
+ else
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x2, 0x2);
+
+ if (snd_soc_read(codec, TABLA_A_CDC_MBHC_B1_CTL) & 0x4) {
+ if (!(tabla->clock_active)) {
+ tabla_codec_enable_config_mode(codec, 1);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
+ 0x04, 0);
+ usleep_range(calibration->shutdown_plug_removal,
+ calibration->shutdown_plug_removal);
+ tabla_codec_enable_config_mode(codec, 0);
+ } else
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
+ 0x04, 0);
+ }
+
+ snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0xC,
+ calibration->hph_current << 2);
+
+ snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x13);
+
+ switch (calibration->bias) {
+ case TABLA_MICBIAS1:
+ micbias_mbhc_reg = TABLA_A_MICB_1_MBHC;
+ micbias_int_reg = TABLA_A_MICB_1_INT_RBIAS;
+ micbias_ctl_reg = TABLA_A_MICB_1_CTL;
+ break;
+ case TABLA_MICBIAS2:
+ micbias_mbhc_reg = TABLA_A_MICB_2_MBHC;
+ micbias_int_reg = TABLA_A_MICB_2_INT_RBIAS;
+ micbias_ctl_reg = TABLA_A_MICB_2_CTL;
+ break;
+ case TABLA_MICBIAS3:
+ micbias_mbhc_reg = TABLA_A_MICB_3_MBHC;
+ micbias_int_reg = TABLA_A_MICB_3_INT_RBIAS;
+ micbias_ctl_reg = TABLA_A_MICB_3_CTL;
+ break;
+ case TABLA_MICBIAS4:
+ micbias_mbhc_reg = TABLA_A_MICB_4_MBHC;
+ micbias_int_reg = TABLA_A_MICB_4_INT_RBIAS;
+ micbias_ctl_reg = TABLA_A_MICB_4_CTL;
+ break;
+ default:
+ pr_err("Error, invalid mic bias line\n");
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, micbias_int_reg, 0x80, 0);
+ snd_soc_update_bits(codec, micbias_ctl_reg, 0x1, 0);
+
+ /* If central bandgap disabled */
+ if (!(snd_soc_read(codec, TABLA_A_PIN_CTL_OE1) & 1)) {
+ snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE1, 0x3, 0x3);
+ usleep_range(calibration->bg_fast_settle,
+ calibration->bg_fast_settle);
+ central_bias_enabled = 1;
+ }
+
+ /* If LDO_H disabled */
+ if (snd_soc_read(codec, TABLA_A_PIN_CTL_OE0) & 0x80) {
+ snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE0, 0x10, 0);
+ snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE0, 0x80, 0x80);
+ usleep_range(calibration->tldoh, calibration->tldoh);
+ snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE0, 0x80, 0);
+
+ if (central_bias_enabled)
+ snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE1, 0x1, 0);
+ }
+ snd_soc_update_bits(codec, micbias_mbhc_reg, 0x60,
+ calibration->mic_current << 5);
+ snd_soc_update_bits(codec, micbias_mbhc_reg, 0x80, 0x80);
+ usleep_range(calibration->mic_pid, calibration->mic_pid);
+ snd_soc_update_bits(codec, micbias_mbhc_reg, 0x10, 0x10);
+
+ snd_soc_update_bits(codec, TABLA_A_MICB_4_MBHC, 0x3, calibration->bias);
+
+ tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
+ return 0;
+}
+
+int tabla_hs_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
+ struct tabla_mbhc_calibration *calibration)
+{
+ struct tabla_priv *tabla;
+ if (!codec || !calibration) {
+ pr_err("Error: no codec or calibration\n");
+ return -EINVAL;
+ }
+ tabla = snd_soc_codec_get_drvdata(codec);
+ tabla->jack = jack;
+ tabla->calibration = calibration;
+
+ return tabla_codec_enable_hs_detect(codec, 1);
+}
+EXPORT_SYMBOL_GPL(tabla_hs_detect);
+
+static irqreturn_t tabla_dummy_handler(int irq, void *data)
+{
+ struct tabla_priv *priv = data;
+ struct snd_soc_codec *codec = priv->codec;
+ snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x1);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tabla_hs_insert_irq(int irq, void *data)
+{
+ struct tabla_priv *priv = data;
+ struct snd_soc_codec *codec = priv->codec;
+
+ tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+
+ if (priv->jack) {
+ pr_debug("reporting insertion %d\n", SND_JACK_HEADSET);
+ snd_soc_jack_report(priv->jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET);
+ }
+ usleep_range(priv->calibration->setup_plug_removal_delay,
+ priv->calibration->setup_plug_removal_delay);
+
+ tabla_codec_setup_hs_polling(codec);
+
+ return IRQ_HANDLED;
+}
+
+static void tabla_codec_shutdown_hs_polling(struct snd_soc_codec *codec)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ if (!tabla->ref_cnt) {
+ snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS, 0xE0, 0x00);
+ tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_AUDIO_MODE);
+ tabla_codec_enable_clock_block(codec, 0);
+ }
+
+ tabla->mbhc_polling_active = false;
+}
+
+static irqreturn_t tabla_hs_remove_irq(int irq, void *data)
+{
+ struct tabla_priv *priv = data;
+ struct snd_soc_codec *codec = priv->codec;
+
+ tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
+ tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+
+ usleep_range(priv->calibration->shutdown_plug_removal,
+ priv->calibration->shutdown_plug_removal);
+
+ if (priv->jack) {
+ pr_debug("reporting removal\n");
+ snd_soc_jack_report(priv->jack, 0, SND_JACK_HEADSET);
+ }
+ tabla_codec_shutdown_hs_polling(codec);
+
+ tabla_codec_enable_hs_detect(codec, 1);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned long slimbus_value;
+
+static irqreturn_t tabla_slimbus_irq(int irq, void *data)
+{
+ struct tabla_priv *priv = data;
+ struct snd_soc_codec *codec = priv->codec;
+ int i, j;
+ u8 val;
+
+ for (i = 0; i < TABLA_SLIM_NUM_PORT_REG; i++) {
+ slimbus_value = tabla_interface_reg_read(codec->control_data,
+ TABLA_SLIM_PGD_PORT_INT_STATUS0 + i);
+ for_each_set_bit(j, &slimbus_value, BITS_PER_BYTE) {
+ val = tabla_interface_reg_read(codec->control_data,
+ TABLA_SLIM_PGD_PORT_INT_SOURCE0 + i*8 + j);
+ if (val & 0x1)
+ pr_err_ratelimited("overflow error on port %x,"
+ " value %x\n", i*8 + j, val);
+ if (val & 0x2)
+ pr_err_ratelimited("underflow error on port %x,"
+ " value %x\n", i*8 + j, val);
+ }
+ tabla_interface_reg_write(codec->control_data,
+ TABLA_SLIM_PGD_PORT_INT_CLR0 + i, 0xFF);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int tabla_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tabla *control;
+ struct tabla_priv *tabla;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ int ret = 0;
+ int i;
+ int tx_channel;
+
+ codec->control_data = dev_get_drvdata(codec->dev->parent);
+ control = codec->control_data;
+
+ tabla = kzalloc(sizeof(struct tabla_priv), GFP_KERNEL);
+ if (!tabla) {
+ dev_err(codec->dev, "Failed to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ snd_soc_codec_set_drvdata(codec, tabla);
+
+ tabla->ref_cnt = 0;
+ tabla->bandgap_type = TABLA_BANDGAP_OFF;
+ tabla->clock_active = false;
+ tabla->config_mode_active = false;
+ tabla->mbhc_polling_active = false;
+ tabla->codec = codec;
+
+ /* TODO only enable bandgap when necessary in order to save power */
+ tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_AUDIO_MODE);
+ tabla_codec_enable_clock_block(codec, 0);
+
+ /* Initialize gain registers to use register gain */
+ snd_soc_update_bits(codec, TABLA_A_RX_HPH_L_GAIN, 0x10, 0x10);
+ snd_soc_update_bits(codec, TABLA_A_RX_HPH_R_GAIN, 0x10, 0x10);
+ snd_soc_update_bits(codec, TABLA_A_RX_LINE_1_GAIN, 0x10, 0x10);
+ snd_soc_update_bits(codec, TABLA_A_RX_LINE_3_GAIN, 0x10, 0x10);
+
+ /* Initialize mic biases to differential mode */
+ snd_soc_update_bits(codec, TABLA_A_MICB_1_INT_RBIAS, 0x24, 0x24);
+ snd_soc_update_bits(codec, TABLA_A_MICB_2_INT_RBIAS, 0x24, 0x24);
+ snd_soc_update_bits(codec, TABLA_A_MICB_3_INT_RBIAS, 0x24, 0x24);
+ snd_soc_update_bits(codec, TABLA_A_MICB_4_INT_RBIAS, 0x24, 0x24);
+
+ /* Set headset CFILT to fast mode */
+ snd_soc_update_bits(codec, TABLA_A_MICB_CFILT_1_CTL, 0x00, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_MICB_CFILT_2_CTL, 0x00, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_MICB_CFILT_3_CTL, 0x00, 0x00);
+
+ snd_soc_update_bits(codec, TABLA_A_CDC_CONN_CLSG_CTL, 0x30, 0x10);
+
+ /* Use 16 bit sample size for now */
+ for (tx_channel = 0; tx_channel < 6; tx_channel++) {
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_CONN_TX_SB_B1_CTL + tx_channel, 0x30, 0x20);
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_TX1_MUX_CTL + tx_channel, 0x8, 0x0);
+
+ }
+ for (tx_channel = 6; tx_channel < 10; tx_channel++) {
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_CONN_TX_SB_B1_CTL + tx_channel, 0x60, 0x40);
+ snd_soc_update_bits(codec,
+ TABLA_A_CDC_TX1_MUX_CTL + tx_channel, 0x8, 0x0);
+ }
+ snd_soc_write(codec, TABLA_A_CDC_CONN_RX_SB_B1_CTL, 0xAA);
+ snd_soc_write(codec, TABLA_A_CDC_CONN_RX_SB_B2_CTL, 0xAA);
+
+ snd_soc_add_controls(codec, tabla_snd_controls,
+ ARRAY_SIZE(tabla_snd_controls));
+ snd_soc_dapm_new_controls(dapm, tabla_dapm_widgets,
+ ARRAY_SIZE(tabla_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+ snd_soc_dapm_sync(dapm);
+
+ ret = tabla_request_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION,
+ tabla_hs_insert_irq, "Headset insert detect", tabla);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ TABLA_IRQ_MBHC_INSERTION);
+ goto err_insert_irq;
+ }
+ tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+
+ ret = tabla_request_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL,
+ tabla_hs_remove_irq, "Headset remove detect", tabla);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ TABLA_IRQ_MBHC_REMOVAL);
+ goto err_remove_irq;
+ }
+ tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
+
+ ret = tabla_request_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL,
+ tabla_dummy_handler, "DC Estimation detect", tabla);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ TABLA_IRQ_MBHC_POTENTIAL);
+ goto err_potential_irq;
+ }
+ tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+
+ ret = tabla_request_irq(codec->control_data, TABLA_IRQ_SLIMBUS,
+ tabla_slimbus_irq, "SLIMBUS Slave", tabla);
+ if (ret) {
+ pr_err("%s: Failed to request irq %d\n", __func__,
+ TABLA_IRQ_SLIMBUS);
+ goto err_slimbus_irq;
+ }
+
+ for (i = 0; i < TABLA_SLIM_NUM_PORT_REG; i++)
+ tabla_interface_reg_write(codec->control_data,
+ TABLA_SLIM_PGD_PORT_INT_EN0 + i, 0xFF);
+
+ return ret;
+
+err_slimbus_irq:
+ tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
+err_potential_irq:
+ tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
+err_remove_irq:
+ tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
+err_insert_irq:
+ kfree(tabla);
+ return ret;
+}
+static int tabla_codec_remove(struct snd_soc_codec *codec)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ tabla_free_irq(codec->control_data, TABLA_IRQ_SLIMBUS, tabla);
+ tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
+ tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
+ tabla_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
+ tabla_codec_disable_clock_block(codec);
+ tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_OFF);
+ kfree(tabla);
+ return 0;
+}
+static struct snd_soc_codec_driver soc_codec_dev_tabla = {
+ .probe = tabla_codec_probe,
+ .remove = tabla_codec_remove,
+ .read = tabla_read,
+ .write = tabla_write,
+
+ .readable_register = tabla_readable,
+ .volatile_register = tabla_volatile,
+
+ .reg_cache_size = TABLA_CACHE_SIZE,
+ .reg_cache_default = tabla_reg_defaults,
+ .reg_word_size = 1,
+};
+static int __devinit tabla_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tabla,
+ tabla_dai, ARRAY_SIZE(tabla_dai));
+}
+static int __devexit tabla_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+static struct platform_driver tabla_codec_driver = {
+ .probe = tabla_probe,
+ .remove = tabla_remove,
+ .driver = {
+ .name = "tabla_codec",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init tabla_codec_init(void)
+{
+ return platform_driver_register(&tabla_codec_driver);
+}
+
+static void __exit tabla_codec_exit(void)
+{
+ platform_driver_unregister(&tabla_codec_driver);
+}
+
+module_init(tabla_codec_init);
+module_exit(tabla_codec_exit);
+
+MODULE_DESCRIPTION("Tabla codec driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
new file mode 100644
index 0000000..9b4faef
--- /dev/null
+++ b/sound/soc/codecs/wcd9310.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <sound/soc.h>
+
+#define TABLA_NUM_REGISTERS 0x400
+#define TABLA_MAX_REGISTER (TABLA_NUM_REGISTERS-1)
+#define TABLA_CACHE_SIZE TABLA_NUM_REGISTERS
+
+extern const u8 tabla_reg_readable[TABLA_CACHE_SIZE];
+extern const u8 tabla_reg_defaults[TABLA_CACHE_SIZE];
+
+enum tabla_micbias_num {
+ TABLA_MICBIAS1,
+ TABLA_MICBIAS2,
+ TABLA_MICBIAS3,
+ TABLA_MICBIAS4,
+};
+
+enum tabla_pid_current {
+ TABLA_PID_MIC_2P5_UA,
+ TABLA_PID_MIC_5_UA,
+ TABLA_PID_MIC_10_UA,
+ TABLA_PID_MIC_20_UA,
+};
+
+struct tabla_mbhc_calibration {
+ enum tabla_micbias_num bias;
+ int tldoh;
+ int bg_fast_settle;
+ enum tabla_pid_current mic_current;
+ int mic_pid;
+ enum tabla_pid_current hph_current;
+ int setup_plug_removal_delay;
+ int shutdown_plug_removal;
+};
+
+extern int tabla_hs_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack, struct tabla_mbhc_calibration *calibration);
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c
index 309c59e..25e9e62 100644
--- a/sound/soc/imx/imx-pcm-fiq.c
+++ b/sound/soc/imx/imx-pcm-fiq.c
@@ -8,8 +8,7 @@
*
* 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.
+ * Free Software Foundation; only version 2 of the License.
*/
#include <linux/clk.h>
#include <linux/delay.h>
diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
index a7c9578..81153b1 100644
--- a/sound/soc/jz4740/jz4740-pcm.c
+++ b/sound/soc/jz4740/jz4740-pcm.c
@@ -3,8 +3,7 @@
*
* 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.
+ * Free Software Foundation; only version 2 of the License.
*
* 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.,
@@ -370,4 +369,4 @@
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c
index cd33de1..b21a5e4 100644
--- a/sound/soc/kirkwood/kirkwood-dma.c
+++ b/sound/soc/kirkwood/kirkwood-dma.c
@@ -6,8 +6,7 @@
*
* 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.
+ * Free Software Foundation; only version 2 of the License.
*/
#include <linux/init.h>
@@ -402,5 +401,5 @@
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:kirkwood-pcm-audio");
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
new file mode 100644
index 0000000..485d3db5
--- /dev/null
+++ b/sound/soc/msm/Kconfig
@@ -0,0 +1,134 @@
+menu "MSM SoC Audio support"
+
+#7201 7625 variants
+config SND_MSM_DAI_SOC
+ tristate
+
+config SND_MSM_SOC_MSM7K
+ tristate
+
+config SND_MSM_SOC
+ tristate "SoC Audio for the MSM series chips"
+ depends on ARCH_MSM_ARM11 && SND_SOC && MSM_ADSP
+ select SND_MSM_DAI_SOC
+ select SND_MSM_SOC_MSM7K
+ default n
+ help
+ To add support for ALSA PCM driver for MSM board.
+
+#7630 Variants
+config SND_MSM7KV2_DAI_SOC
+ tristate
+
+config SND_MSM_SOC_MSM7KV2
+ tristate
+
+config SND_MSM7KV2_SOC
+ tristate "SoC Audio for the MSM7KV2 chip"
+ depends on ARCH_MSM7X30 && SND_SOC && MSM7KV2_AUDIO
+ select SND_MSM_SOC_MSM7KV2
+ select SND_MSM7KV2_DAI_SOC
+ default n
+ help
+ To add support for ALSA PCM driver for QSD8k board.
+
+config SND_MSM_MVS7x30_SOC
+ tristate
+
+config SND_MSM_MVS_DAI_SOC
+ tristate
+
+config SND_MVS_SOC
+ tristate "SoC Mvs support for MSM7X30"
+ depends on SND_MSM7KV2_SOC
+ select SND_MSM_MVS7x30_SOC
+ select SND_MSM_MVS_DAI_SOC
+ default n
+ help
+ To support Mvs packet capture/playback
+
+#8660 Variants
+config SND_SOC_MSM8X60_PCM
+ tristate
+
+config SND_SOC_MSM8X60_DAI
+ tristate
+
+config SND_SOC_MSM8X60
+ tristate "SoC Audio over DSP support for MSM8660"
+ depends on ARCH_MSM8X60 && SND_SOC && MSM8X60_AUDIO
+ select SND_SOC_MSM8X60_PCM
+ select SND_SOC_MSM8X60_DAI
+ select SND_SOC_MSM_QDSP6_INTF
+ default y
+ help
+ To add support for SoC audio on MSM8X60. This driver
+ Adds support for audio over DSP. The driver adds Kcontrols
+ to do device switch/routing and volume control support for all
+ audio sessions. The kcontols also does sesion management for
+ voice calls
+
+config SND_SOC_MSM_HOSTLESS_PCM
+ tristate
+
+config SND_SOC_MSM8660_PCM
+ tristate
+
+config SND_SOC_MSM8660_LPAIF
+ tristate
+
+config SND_SOC_MSM8660
+ tristate "SoC Machine driver for MSM8660"
+ depends on !SND_SOC_MSM8X60 && ARCH_MSM8X60
+ select SND_SOC_MSM8660_PCM
+ select SND_SOC_MSM8660_LPAIF
+ select SND_SOC_TIMPANI
+ select MARIMBA_CORE
+ select SND_SOC_MSM_QDSP6_INTF
+ default n
+ help
+ To add support for SoC audio on MSM8660 for direct playback
+ to LPA buffer over DMA.The interface bypasses DSP and hence
+ does not support any post/pre processing features.The driver
+ would support full duplex playback/record sessions.
+
+config SND_VOIP_PCM
+ tristate
+
+config MSM_8x60_VOIP
+ tristate "SoC Machine driver for voip"
+ depends on SND_SOC_MSM8X60
+ select SND_MSM_MVS_DAI_SOC
+ select SND_VOIP_PCM
+ default n
+ help
+ To support ALSA VOIP driver for MSM8x60 target.
+ This driver communicates with QDSP6, for getting
+ uplink and downlink voice packets.
+
+config SND_SOC_MSM_QDSP6_INTF
+ bool "SoC Q6 audio driver for MSM8960"
+ depends on MSM_QDSP6_APR
+ default n
+ help
+ To add support for SoC audio on MSM8960.
+
+config SND_SOC_QDSP6
+ tristate "SoC ALSA audio driver for QDSP6"
+ select SND_SOC_MSM_QDSP6_INTF
+ default n
+ help
+ To add support for MSM QDSP6 Soc Audio.
+
+config SND_SOC_MSM8960
+ tristate "SoC Machine driver for MSM8960 boards"
+ depends on ARCH_MSM8960
+ select SND_SOC_QDSP6
+ select SND_SOC_MSM_STUB
+ select SND_SOC_WCD9310
+ select SND_SOC_MSM_HOSTLESS_PCM
+ default n
+ help
+ To add support for SoC audio on MSM8960 boards
+
+endmenu
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
new file mode 100644
index 0000000..92b008d
--- /dev/null
+++ b/sound/soc/msm/Makefile
@@ -0,0 +1,71 @@
+# MSM CPU/CODEC DAI Support
+snd-soc-msm-dai-objs := msm-dai.o
+obj-$(CONFIG_SND_MSM_DAI_SOC) += snd-soc-msm-dai.o
+
+snd-soc-msm7kv2-dai-objs := msm7kv2-dai.o
+obj-$(CONFIG_SND_MSM7KV2_DAI_SOC) += snd-soc-msm7kv2-dai.o
+
+# MSM Platform Support
+snd-soc-msm-objs := msm-pcm.o msm7k-pcm.o
+obj-$(CONFIG_SND_MSM_SOC) += snd-soc-msm.o
+
+snd-soc-msmv2-objs := msm7kv2-dsp.o msm7kv2-pcm.o
+obj-$(CONFIG_SND_MSM7KV2_SOC) += snd-soc-msmv2.o
+
+# MSM Machine Support
+snd-soc-msm7k-objs := msm7201.o
+obj-$(CONFIG_SND_MSM_SOC_MSM7K) += snd-soc-msm7k.o
+
+snd-soc-msm7kv2-objs := msm7x30.o
+obj-$(CONFIG_SND_MSM_SOC_MSM7KV2) += snd-soc-msm7kv2.o
+
+# 8660 ALSA Support
+snd-soc-msm8x60-dai-objs := msm8x60-dai.o
+obj-$(CONFIG_SND_SOC_MSM8X60_DAI) += snd-soc-msm8x60-dai.o
+
+snd-soc-msm8x60-pcm-objs := msm8x60-pcm.o
+obj-$(CONFIG_SND_SOC_MSM8X60_PCM) += snd-soc-msm8x60-pcm.o
+
+snd-soc-msm8x60-objs := msm8x60.o
+obj-$(CONFIG_SND_SOC_MSM8X60) += snd-soc-msm8x60.o
+
+
+#MVS Support
+snd-soc-msm-mvs-dai-objs := mvs-dai.o
+obj-$(CONFIG_SND_MSM_MVS_DAI_SOC) += snd-soc-msm-mvs-dai.o
+
+snd-soc-msm-mvs-objs := msm-mvs.o
+obj-$(CONFIG_SND_MVS_SOC) += snd-soc-msm-mvs.o
+
+# 8660 ALSA Support
+snd-soc-msm8660-lpa-objs := msm8660-i2s.o msm8660-dma.o
+obj-$(CONFIG_SND_SOC_MSM8660_LPAIF) += snd-soc-msm8660-lpa.o
+
+snd-soc-msm8660-pcm-objs := msm8660-pcm.o
+obj-$(CONFIG_SND_SOC_MSM8660_PCM) += snd-soc-msm8660-pcm.o
+
+snd-soc-msm8660-objs := msm8660.o
+obj-$(CONFIG_SND_SOC_MSM8660) += snd-soc-msm8660.o
+
+#8660 VOIP Driver Support
+
+snd-soc-msm-voip-objs := msm-voip.o
+obj-$(CONFIG_SND_VOIP_PCM) += snd-soc-msm-voip.o
+
+snd-soc-msm8660-dma-objs := msm8660-dma.o
+obj-$(CONFIG_SND_SOC_MSM8X60) += snd-soc-msm8660-dma.o
+
+# for MSM 8960 sound card driver
+
+obj-$(CONFIG_SND_SOC_MSM_QDSP6_INTF) += qdsp6/
+
+snd-soc-qdsp6-objs := msm-dai-q6.o msm-pcm-q6.o msm-pcm-routing.o msm-dai-fe.o msm-pcm-voice.o msm-pcm-voip.o
+snd-soc-qdsp6-objs += msm-pcm-lpa.o
+obj-$(CONFIG_SND_SOC_QDSP6) += snd-soc-qdsp6.o
+
+snd-soc-msm8960-objs := msm8960.o
+obj-$(CONFIG_SND_SOC_MSM8960) += snd-soc-msm8960.o
+
+# Generic MSM drivers
+snd-soc-hostless-pcm-objs := msm-pcm-hostless.o
+obj-$(CONFIG_SND_SOC_MSM_HOSTLESS_PCM) += snd-soc-hostless-pcm.o
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
new file mode 100644
index 0000000..c937a5a
--- /dev/null
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -0,0 +1,210 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+static struct snd_soc_dai_ops msm_fe_dai_ops = {};
+
+static struct snd_soc_dai_driver msm_fe_dais[] = {
+ {
+ .playback = {
+ .stream_name = "Multimedia1 Playback",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "Multimedia1 Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "MultiMedia1",
+ },
+ {
+ .playback = {
+ .stream_name = "Multimedia2 Playback",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "Multimedia2 Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "MultiMedia2",
+ },
+ {
+ .playback = {
+ .stream_name = "Voice Playback",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "Voice Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "CS-VOICE",
+ },
+ {
+ .playback = {
+ .stream_name = "VoIP Playback",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "VoIP Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "VoIP",
+ },
+ {
+ .playback = {
+ .stream_name = "MultiMedia3 Playback",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "MultiMedia3",
+ },
+ /* FE DAIs created for hostless operation purpose */
+ {
+ .playback = {
+ .stream_name = "SLIMBUS0 Hostless Playback",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "SLIMBUS0 Hostless Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SLIMBUS0_HOSTLESS",
+ },
+ {
+ .playback = {
+ .stream_name = "INT_FM Hostless Playback",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "INT_FM Hostless Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "INT_FM_HOSTLESS",
+ },
+};
+
+static __devinit int msm_fe_dai_dev_probe(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "%s: dev name %s\n", __func__,
+ dev_name(&pdev->dev));
+ return snd_soc_register_dais(&pdev->dev, msm_fe_dais,
+ ARRAY_SIZE(msm_fe_dais));
+}
+
+static __devexit int msm_fe_dai_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_fe_dai_driver = {
+ .probe = msm_fe_dai_dev_probe,
+ .remove = msm_fe_dai_dev_remove,
+ .driver = {
+ .name = "msm-dai-fe",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_fe_dai_init(void)
+{
+ return platform_driver_register(&msm_fe_dai_driver);
+}
+module_init(msm_fe_dai_init);
+
+static void __exit msm_fe_dai_exit(void)
+{
+ platform_driver_unregister(&msm_fe_dai_driver);
+}
+module_exit(msm_fe_dai_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM Frontend DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
new file mode 100644
index 0000000..9cdd1d6
--- /dev/null
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -0,0 +1,555 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wcd9310/core.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio.h>
+#include <sound/q6afe.h>
+#include <sound/q6adm.h>
+
+enum {
+ STATUS_PORT_STARTED, /* track if AFE port has started */
+ STATUS_MAX
+};
+
+struct msm_dai_q6_dai_data {
+ DECLARE_BITMAP(status_mask, STATUS_MAX);
+ u32 rate;
+ u32 channels;
+ union afe_port_config port_config;
+};
+
+static int msm_dai_q6_cdc_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ switch (dai_data->channels) {
+ case 2:
+ dai_data->port_config.mi2s.channel = MSM_AFE_STEREO;
+ break;
+ case 1:
+ dai_data->port_config.mi2s.channel = MSM_AFE_MONO;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ dai_data->rate = params_rate(params);
+
+ dev_dbg(dai->dev, " channel %d sample rate %d entered\n",
+ dai_data->channels, dai_data->rate);
+
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.mi2s.bitwidth = 16;
+ dai_data->port_config.mi2s.line = 1;
+ dai_data->port_config.mi2s.ws = 1; /* I2S master mode for now */
+
+ return 0;
+}
+
+static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dev_dbg(dai->dev, "%s start HDMI port\n", __func__);
+
+ dai_data->channels = params_channels(params);
+ switch (dai_data->channels) {
+ case 2:
+ dai_data->port_config.hdmi.channel_mode = 0; /* Put in macro */
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.hdmi.bitwidth = 16;
+ dai_data->port_config.hdmi.data_type = 0;
+ dai_data->rate = params_rate(params);
+
+ return 0;
+}
+
+static int msm_dai_q6_slim_bus_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ u8 pgd_la, inf_la;
+
+ memset(dai_data->port_config.slimbus.slave_port_mapping, 0,
+ sizeof(dai_data->port_config.slimbus.slave_port_mapping));
+
+ dai_data->channels = params_channels(params);
+ switch (dai_data->channels) {
+ case 2:
+ if (dai->id == SLIMBUS_0_RX) {
+ dai_data->port_config.slimbus.slave_port_mapping[0] = 1;
+ dai_data->port_config.slimbus.slave_port_mapping[1] = 2;
+ } else {
+ dai_data->port_config.slimbus.slave_port_mapping[0] = 7;
+ dai_data->port_config.slimbus.slave_port_mapping[1] = 8;
+ }
+ break;
+ case 1:
+ if (dai->id == SLIMBUS_0_RX)
+ dai_data->port_config.slimbus.slave_port_mapping[0] = 1;
+ else
+ dai_data->port_config.slimbus.slave_port_mapping[0] = 7;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ dai_data->rate = params_rate(params);
+ tabla_get_logical_addresses(&pgd_la, &inf_la);
+
+ dai_data->port_config.slimbus.slimbus_dev_id = AFE_SLIMBUS_DEVICE_1;
+ dai_data->port_config.slimbus.slave_dev_pgd_la = pgd_la;
+ dai_data->port_config.slimbus.slave_dev_intfdev_la = inf_la;
+ /* Q6 only supports 16 as now */
+ dai_data->port_config.slimbus.bit_width = 16;
+ dai_data->port_config.slimbus.data_format = 0;
+ dai_data->port_config.slimbus.num_channels = dai_data->channels;
+ dai_data->port_config.slimbus.reserved = 0;
+
+ dev_dbg(dai->dev, "slimbus_dev_id %hu slave_dev_pgd_la 0x%hx\n"
+ "slave_dev_intfdev_la 0x%hx bit_width %hu data_format %hu\n"
+ "num_channel %hu slave_port_mapping[0] %hu\n"
+ "slave_port_mapping[1] %hu slave_port_mapping[2] %hu\n"
+ "sample_rate %d\n",
+ dai_data->port_config.slimbus.slimbus_dev_id,
+ dai_data->port_config.slimbus.slave_dev_pgd_la,
+ dai_data->port_config.slimbus.slave_dev_intfdev_la,
+ dai_data->port_config.slimbus.bit_width,
+ dai_data->port_config.slimbus.data_format,
+ dai_data->port_config.slimbus.num_channels,
+ dai_data->port_config.slimbus.slave_port_mapping[0],
+ dai_data->port_config.slimbus.slave_port_mapping[1],
+ dai_data->port_config.slimbus.slave_port_mapping[2],
+ dai_data->rate);
+
+ return 0;
+}
+
+static int msm_dai_q6_bt_fm_hw_params(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai, int stream)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+ dai_data->channels = params_channels(params);
+ dai_data->rate = params_rate(params);
+
+ dev_dbg(dai->dev, "channels %d sample rate %d entered\n",
+ dai_data->channels, dai_data->rate);
+
+ memset(&dai_data->port_config, 0, sizeof(dai_data->port_config));
+
+ return 0;
+}
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int rc = 0;
+
+ switch (dai->id) {
+ case PRIMARY_I2S_TX:
+ case PRIMARY_I2S_RX:
+ rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream);
+ break;
+ case HDMI_RX:
+ rc = msm_dai_q6_hdmi_hw_params(params, dai);
+ break;
+
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ rc = msm_dai_q6_slim_bus_hw_params(params, dai,
+ substream->stream);
+ break;
+ case INT_BT_SCO_RX:
+ case INT_BT_SCO_TX:
+ case INT_FM_RX:
+ case INT_FM_TX:
+ rc = msm_dai_q6_bt_fm_hw_params(params, dai, substream->stream);
+ break;
+ default:
+ dev_err(dai->dev, "invalid AFE port ID\n");
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static void msm_dai_q6_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc;
+
+ rc = adm_close(dai->id);
+
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close ADM COPP\n");
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(dai->id); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ }
+};
+
+static int msm_dai_q6_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ /* if AFE port is already started, this means
+ * application wishes to restore hardware to
+ * fresh state. This logic anticipates prepare is not
+ * called right after TRIGGER_START before Q6 AFE
+ * has enough time to respond to port start command.
+ */
+ rc = afe_close(dai->id); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+ clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+ } else {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rc = adm_open_mixer(dai->id, 1, dai_data->rate,
+ dai_data->channels, DEFAULT_COPP_TOPOLOGY);
+ else
+ rc = adm_open_mixer(dai->id, 2, dai_data->rate,
+ dai_data->channels, DEFAULT_COPP_TOPOLOGY);
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to open ADM\n");
+ }
+
+ return rc;
+
+}
+
+static int msm_dai_q6_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int rc = 0;
+
+ /* Start/stop port without waiting for Q6 AFE response. Need to have
+ * native q6 AFE driver propagates AFE response in order to handle
+ * port start/stop command error properly if error does arise.
+ */
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ afe_port_start_nowait(dai->id, &dai_data->port_config,
+ dai_data->rate);
+ set_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ afe_port_stop_nowait(dai->id);
+ clear_bit(STATUS_PORT_STARTED,
+ dai_data->status_mask);
+ }
+ break;
+
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc = 0;
+
+ dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data),
+ GFP_KERNEL);
+
+ if (!dai_data) {
+ dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+ dai->id);
+ rc = -ENOMEM;
+ } else
+ dev_set_drvdata(dai->dev, dai_data);
+
+ return rc;
+}
+
+static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
+{
+ struct msm_dai_q6_dai_data *dai_data;
+ int rc;
+
+ dai_data = dev_get_drvdata(dai->dev);
+
+ /* If AFE port is still up, close it */
+ if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+ rc = afe_close(dai->id); /* can block */
+ if (IS_ERR_VALUE(rc))
+ dev_err(dai->dev, "fail to close AFE port\n");
+ }
+ kfree(dai_data);
+ snd_soc_unregister_dai(dai->dev);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_ops = {
+ /*
+ * DSP only handles 16-bit and support only I2S
+ * master mode for now. leave set_fmt function
+ * unimplemented for now.
+ */
+ .prepare = msm_dai_q6_prepare,
+ .trigger = msm_dai_q6_trigger,
+ .hw_params = msm_dai_q6_hw_params,
+ .shutdown = msm_dai_q6_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_i2s_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_i2s_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_hdmi_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_tx_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_sco_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_sco_tx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_max = 16000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
+/* To do: change to register DAIs as batch */
+static __devinit int msm_dai_q6_dev_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+
+ switch (pdev->id) {
+ case PRIMARY_I2S_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_rx_dai);
+ break;
+ case PRIMARY_I2S_TX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_tx_dai);
+ break;
+ case HDMI_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_hdmi_rx_dai);
+ break;
+ case SLIMBUS_0_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_rx_dai);
+ break;
+ case SLIMBUS_0_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_slimbus_tx_dai);
+ case INT_BT_SCO_RX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_bt_sco_rx_dai);
+ break;
+ case INT_BT_SCO_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_bt_sco_tx_dai);
+ break;
+ case INT_FM_RX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_rx_dai);
+ break;
+ case INT_FM_TX:
+ rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_fm_tx_dai);
+ break;
+ default:
+ rc = -ENODEV;
+ break;
+ }
+ return rc;
+}
+
+static __devexit int msm_dai_q6_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_dai_q6_driver = {
+ .probe = msm_dai_q6_dev_probe,
+ .remove = msm_dai_q6_dev_remove,
+ .driver = {
+ .name = "msm-dai-q6",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_dai_q6_init(void)
+{
+ return platform_driver_register(&msm_dai_q6_driver);
+}
+module_init(msm_dai_q6_init);
+
+static void __exit msm_dai_q6_exit(void)
+{
+ platform_driver_unregister(&msm_dai_q6_driver);
+}
+module_exit(msm_dai_q6_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-dai.c b/sound/soc/msm/msm-dai.c
new file mode 100644
index 0000000..6ebee51
--- /dev/null
+++ b/sound/soc/msm/msm-dai.c
@@ -0,0 +1,154 @@
+/* sound/soc/msm/msm-dai.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * Derived from msm-pcm.c and msm7201.c.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#ifdef CONFIG_ARCH_MSM_ARM11
+#include "msm-pcm.h"
+#else
+#include "qsd-pcm.h"
+#endif
+
+static struct snd_soc_dai_driver msm_pcm_codec_dais[] = {
+{
+ .name = "msm-codec-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_max = USE_CHANNELS_MAX,
+ .rates = USE_RATE,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .formats = USE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_max = USE_CHANNELS_MAX,
+ .rate_min = USE_RATE_MIN,
+ .rates = USE_RATE,
+ .formats = USE_FORMATS,
+ },
+},
+};
+
+static struct snd_soc_dai_driver msm_pcm_cpu_dais[] = {
+{
+ .name = "msm-cpu-dai",
+ .playback = {
+ .channels_min = USE_CHANNELS_MIN,
+ .channels_max = USE_CHANNELS_MAX,
+ .rates = USE_RATE,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .formats = USE_FORMATS,
+ },
+ .capture = {
+ .channels_min = USE_CHANNELS_MIN,
+ .channels_max = USE_CHANNELS_MAX,
+ .rate_min = USE_RATE_MIN,
+ .rates = USE_RATE,
+ .formats = USE_FORMATS,
+ },
+},
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_msm = {
+ .compress_type = SND_SOC_FLAT_COMPRESSION,
+};
+
+static __devinit int asoc_msm_codec_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm,
+ msm_pcm_codec_dais, ARRAY_SIZE(msm_pcm_codec_dais));
+}
+
+static int __devexit asoc_msm_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static __devinit int asoc_pcm_cpu_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_dai(&pdev->dev, msm_pcm_cpu_dais);
+}
+
+static int __devexit asoc_pcm_cpu_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver asoc_codec_dai_driver = {
+ .probe = asoc_msm_codec_probe,
+ .remove = __devexit_p(asoc_msm_codec_remove),
+ .driver = {
+ .name = "msm-codec-dai",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_driver asoc_cpu_dai_driver = {
+ .probe = asoc_pcm_cpu_probe,
+ .remove = __devexit_p(asoc_pcm_cpu_remove),
+ .driver = {
+ .name = "msm-cpu-dai",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_codec_dai_init(void)
+{
+ return platform_driver_register(&asoc_codec_dai_driver);
+}
+
+static void __exit msm_codec_dai_exit(void)
+{
+ platform_driver_unregister(&asoc_codec_dai_driver);
+}
+
+static int __init msm_cpu_dai_init(void)
+{
+ return platform_driver_register(&asoc_cpu_dai_driver);
+}
+
+static void __exit msm_cpu_dai_exit(void)
+{
+ platform_driver_unregister(&asoc_cpu_dai_driver);
+}
+
+module_init(msm_codec_dai_init);
+module_exit(msm_codec_dai_exit);
+module_init(msm_cpu_dai_init);
+module_exit(msm_cpu_dai_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM Codec/Cpu Dai driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-mvs.c b/sound/soc/msm/msm-mvs.c
new file mode 100644
index 0000000..2e7114c
--- /dev/null
+++ b/sound/soc/msm/msm-mvs.c
@@ -0,0 +1,936 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/wakelock.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/debug_mm.h>
+#include "msm_audio_mvs.h"
+
+
+static struct audio_mvs_info_type audio_mvs_info;
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_8000),
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = MVS_MAX_VOC_PKT_SIZE * MVS_MAX_Q_LEN,
+ .period_bytes_min = MVS_MAX_VOC_PKT_SIZE,
+ .period_bytes_max = MVS_MAX_VOC_PKT_SIZE,
+ .periods_min = MVS_MAX_Q_LEN,
+ .periods_max = MVS_MAX_Q_LEN,
+ .fifo_size = 0,
+};
+
+static void snd_pcm_mvs_timer(unsigned long data)
+{
+ struct audio_mvs_info_type *audio = &audio_mvs_info;
+ MM_DBG("%s\n", __func__);
+ if (audio->playback_start) {
+ if (audio->ack_dl_count) {
+ audio->pcm_playback_irq_pos += audio->pcm_count;
+ audio->ack_dl_count--;
+ snd_pcm_period_elapsed(audio->playback_substream);
+ }
+ }
+
+ if (audio->capture_start) {
+ if (audio->ack_ul_count) {
+ audio->pcm_capture_irq_pos += audio->pcm_capture_count;
+ audio->ack_ul_count--;
+ snd_pcm_period_elapsed(audio->capture_substream);
+ }
+ }
+ audio->timer.expires += audio->expiry_delta;
+ add_timer(&audio->timer);
+}
+
+static int audio_mvs_setup_mvs(struct audio_mvs_info_type *audio)
+{
+ int rc = 0;
+ struct audio_mvs_enable_msg enable_msg;
+ MM_DBG("%s\n", __func__);
+
+ /* Enable MVS. */
+
+ memset(&enable_msg, 0, sizeof(enable_msg));
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ enable_msg.enable_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
+ enable_msg.enable_args.mode = cpu_to_be32(MVS_MODE_LINEAR_PCM);
+ enable_msg.enable_args.ul_cb_func_id = (int) NULL;
+ enable_msg.enable_args.dl_cb_func_id = (int) NULL;
+ enable_msg.enable_args.context = cpu_to_be32(MVS_PKT_CONTEXT_ISR);
+
+ msm_rpc_setup_req(&enable_msg.rpc_hdr, MVS_PROG,
+ MVS_VERS, MVS_ENABLE_PROC);
+
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &enable_msg, sizeof(enable_msg));
+
+ if (rc >= 0) {
+ MM_DBG("RPC write for enable done\n");
+
+ rc = wait_event_timeout(audio->wait,
+ (audio->rpc_status !=
+ RPC_STATUS_FAILURE), 1 * HZ);
+
+ if (rc > 0) {
+ MM_DBG("Wait event for enable succeeded\n");
+
+ mutex_lock(&audio->lock);
+ audio->mvs_mode = MVS_MODE_LINEAR_PCM;
+ audio->frame_mode = MVS_FRAME_MODE_PCM_DL;
+ audio->pcm_frame = 0;
+ mutex_unlock(&audio->lock);
+ rc = 0;
+
+ } else
+ MM_ERR("Wait event for enable failed %d\n", rc);
+ } else
+ MM_ERR("RPC write for enable failed %d\n", rc);
+ return rc;
+}
+
+static void audio_mvs_rpc_reply(struct msm_rpc_endpoint *endpoint,
+ uint32_t xid)
+{
+ int rc = 0;
+ struct rpc_reply_hdr reply_hdr;
+ MM_DBG("%s\n", __func__);
+
+ memset(&reply_hdr, 0, sizeof(reply_hdr));
+ reply_hdr.xid = cpu_to_be32(xid);
+ reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
+ reply_hdr.reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+ reply_hdr.data.acc_hdr.accept_stat =
+ cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
+ reply_hdr.data.acc_hdr.verf_flavor = 0;
+ reply_hdr.data.acc_hdr.verf_length = 0;
+
+ rc = msm_rpc_write(endpoint, &reply_hdr, sizeof(reply_hdr));
+
+ if (rc < 0)
+ MM_ERR("RPC write for response failed %d\n", rc);
+}
+
+static void audio_mvs_process_rpc_request(uint32_t procedure, uint32_t xid,
+ void *data, uint32_t length,
+ struct audio_mvs_info_type *audio)
+{
+
+ int rc = 0;
+ uint32_t index;
+ MM_DBG("%s\n", __func__);
+ switch (procedure) {
+ case MVS_EVENT_CB_TYPE_PROC:{
+ struct audio_mvs_cb_func_args *args = data;
+ uint32_t event_type = be32_to_cpu(args->event);
+ uint32_t cmd_status =
+ be32_to_cpu(args->
+ event_data.mvs_ev_command_type.cmd_status);
+ uint32_t mode_status =
+ be32_to_cpu(args->
+ event_data.mvs_ev_mode_type.mode_status);
+ audio_mvs_rpc_reply(audio->rpc_endpt, xid);
+ if (be32_to_cpu(args->valid_ptr)) {
+ if (event_type == AUDIO_MVS_COMMAND) {
+ if (cmd_status == AUDIO_MVS_CMD_SUCCESS)
+ audio->rpc_status = RPC_STATUS_SUCCESS;
+ wake_up(&audio->wait);
+ } else if (event_type == AUDIO_MVS_MODE) {
+ if (mode_status != AUDIO_MVS_MODE_NOT_AVAIL) {
+ audio->rpc_status =
+ RPC_STATUS_SUCCESS;
+ }
+ audio->prepare_ack++;
+ wake_up(&audio->wait);
+ wake_up(&audio->prepare_wait);
+ } else {
+ /*nothing to do */
+ }
+ } else
+ MM_ERR("ALSA: CB event pointer not valid\n");
+ break;
+ }
+ case MVS_PACKET_UL_FN_TYPE_PROC:{
+ uint32_t *cb_data = data;
+ uint32_t pkt_len ;
+ struct audio_mvs_ul_reply ul_reply;
+ MM_DBG("MVS_PACKET_UL_FN_TYPE_PROC\n");
+
+ memset(&ul_reply, 0, sizeof(ul_reply));
+ cb_data++;
+ pkt_len = be32_to_cpu(*cb_data);
+ cb_data++;
+ if (audio->capture_enable) {
+ audio_mvs_info.ack_ul_count++;
+ mutex_lock(&audio->out_lock);
+ index = audio->out_write % MVS_MAX_Q_LEN;
+ memcpy(audio->out[index].voc_pkt, cb_data,
+ pkt_len);
+ audio->out[index].len = pkt_len;
+ audio->out_write++;
+ mutex_unlock(&audio->out_lock);
+ }
+ MM_DBG(" audio->out_read = %d audio->out write = %d\n",
+ audio->out_read, audio->out_write);
+ ul_reply.reply_hdr.xid = cpu_to_be32(xid);
+ ul_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
+ ul_reply.reply_hdr.reply_stat =
+ cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+ ul_reply.reply_hdr.data.acc_hdr.accept_stat =
+ cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
+ ul_reply.reply_hdr.data.acc_hdr.verf_flavor = 0;
+ ul_reply.reply_hdr.data.acc_hdr.verf_length = 0;
+ ul_reply.valid_pkt_status_ptr = cpu_to_be32(0x00000001);
+ ul_reply.pkt_status = cpu_to_be32(0x00000000);
+ rc = msm_rpc_write(audio->rpc_endpt, &ul_reply,
+ sizeof(ul_reply));
+ wake_up(&audio->out_wait);
+ if (rc < 0)
+ MM_ERR("RPC write for UL response failed %d\n",
+ rc);
+ break;
+ }
+ case MVS_PACKET_DL_FN_TYPE_PROC:{
+ struct audio_mvs_dl_reply dl_reply;
+ MM_DBG("MVS_PACKET_DL_FN_TYPE_PROC\n");
+ memset(&dl_reply, 0, sizeof(dl_reply));
+ dl_reply.reply_hdr.xid = cpu_to_be32(xid);
+ dl_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
+ dl_reply.reply_hdr.reply_stat =
+ cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+ dl_reply.reply_hdr.data.acc_hdr.accept_stat =
+ cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
+ dl_reply.reply_hdr.data.acc_hdr.verf_flavor = 0;
+ dl_reply.reply_hdr.data.acc_hdr.verf_length = 0;
+ mutex_lock(&audio->in_lock);
+ if (audio->in_read < audio->in_write
+ && audio->dl_play) {
+ index = audio->in_read % MVS_MAX_Q_LEN;
+ memcpy(&dl_reply.voc_pkt,
+ audio->in[index].voc_pkt,
+ audio->in[index].len);
+ audio->in_read++;
+ audio_mvs_info.ack_dl_count++;
+ dl_reply.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+ wake_up(&audio->in_wait);
+ } else {
+ dl_reply.pkt_status =
+ cpu_to_be32(AUDIO_MVS_PKT_SLOW);
+ }
+ mutex_unlock(&audio->in_lock);
+ MM_DBG(" audio->in_read = %d audio->in write = %d\n",
+ audio->in_read, audio->in_write);
+ dl_reply.valid_frame_info_ptr = cpu_to_be32(0x00000001);
+ dl_reply.frame_mode = cpu_to_be32(audio->frame_mode);
+ dl_reply.frame_mode_again =
+ cpu_to_be32(audio->frame_mode);
+ dl_reply.frame_info_hdr.frame_mode =
+ cpu_to_be32(audio->frame_mode);
+ dl_reply.frame_info_hdr.mvs_mode =
+ cpu_to_be32(audio->mvs_mode);
+ dl_reply.frame_info_hdr.buf_free_cnt = 0;
+ dl_reply.pcm_frame = cpu_to_be32(audio->pcm_frame);
+ dl_reply.pcm_mode = cpu_to_be32(audio->pcm_mode);
+ dl_reply.valid_pkt_status_ptr = cpu_to_be32(0x00000001);
+ rc = msm_rpc_write(audio->rpc_endpt, &dl_reply,
+ sizeof(dl_reply));
+ if (rc < 0)
+ MM_ERR("RPC write for DL response failed %d\n",
+ rc);
+ break;
+ }
+ default:
+ MM_ERR("Unknown CB type %d\n", procedure);
+ }
+}
+
+static int audio_mvs_thread(void *data)
+{
+ struct audio_mvs_info_type *audio = &audio_mvs_info;
+ struct rpc_request_hdr *rpc_hdr = NULL;
+ struct rpc_reply_hdr *rpc_reply = NULL;
+ uint32_t reply_status = 0;
+ uint32_t rpc_type;
+ int rpc_hdr_len;
+ MM_DBG("%s\n", __func__);
+
+ while (!kthread_should_stop()) {
+ rpc_hdr_len =
+ msm_rpc_read(audio->rpc_endpt, (void **)&rpc_hdr, -1, -1);
+ if (rpc_hdr_len < 0) {
+ MM_ERR("RPC read failed %d\n", rpc_hdr_len);
+ break;
+ } else if (rpc_hdr_len < RPC_COMMON_HDR_SZ)
+ continue;
+ else {
+ rpc_type = be32_to_cpu(rpc_hdr->type);
+ if (rpc_type == RPC_TYPE_REPLY) {
+ if (rpc_hdr_len < RPC_REPLY_HDR_SZ)
+ continue;
+ rpc_reply = (void *)rpc_hdr;
+ reply_status = be32_to_cpu(rpc_reply->
+ reply_stat);
+ if (reply_status != RPCMSG_REPLYSTAT_ACCEPTED) {
+ /* If the command is not accepted,
+ * there will be no response callback.
+ * Wake the caller and report error. */
+ audio->rpc_status = RPC_STATUS_REJECT;
+ wake_up(&audio->wait);
+ MM_ERR("RPC reply status denied\n");
+ }
+ } else if (rpc_type == RPC_TYPE_REQUEST) {
+ if (rpc_hdr_len < RPC_REQUEST_HDR_SZ)
+ continue;
+ MM_DBG("ALSA: kthread call procedure\n");
+ audio_mvs_process_rpc_request(
+ be32_to_cpu(rpc_hdr->procedure),
+ be32_to_cpu(rpc_hdr->xid),
+ (void *)(rpc_hdr + 1),
+ (rpc_hdr_len - sizeof(*rpc_hdr)),
+ audio);
+ } else
+ MM_ERR("Unexpected RPC type %d\n", rpc_type);
+ }
+ kfree(rpc_hdr);
+ rpc_hdr = NULL;
+ }
+ return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+
+ struct audio_mvs_info_type *audio = &audio_mvs_info;
+ MM_DBG("%s\n", __func__);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio->playback_start = 1;
+ else
+ audio->capture_start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio->playback_start = 0;
+ else
+ audio->capture_start = 0;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_mvs_info_type *audio = &audio_mvs_info;
+
+ MM_DBG("%s\n", __func__);
+ mutex_lock(&audio->lock);
+ if (audio->state < AUDIO_MVS_OPENED) {
+ audio->rpc_endpt =
+ msm_rpc_connect_compatible(MVS_PROG,
+ MVS_VERS,
+ MSM_RPC_UNINTERRUPTIBLE);
+ audio->state = AUDIO_MVS_OPENED;
+ }
+
+ if (IS_ERR(audio->rpc_endpt)) {
+ MM_ERR("ALSA MVS RPC connect failed with version 0x%x\n",
+ MVS_VERS);
+ ret = PTR_ERR(audio->rpc_endpt);
+ audio->rpc_endpt = NULL;
+ goto err;
+ } else {
+ MM_DBG("ALSA MVS RPC connect succeeded\n");
+ if (audio->playback_substream == NULL ||
+ audio->capture_substream == NULL) {
+ if (substream->stream ==
+ SNDRV_PCM_STREAM_PLAYBACK) {
+ audio->playback_substream =
+ substream;
+ runtime->hw = msm_pcm_hardware;
+ } else if (substream->stream ==
+ SNDRV_PCM_STREAM_CAPTURE) {
+ audio->capture_substream =
+ substream;
+ runtime->hw = msm_pcm_hardware;
+ }
+ } else {
+ ret = -EPERM;
+ goto err;
+ }
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ MM_ERR("snd_pcm_hw_constraint_integer failed\n");
+ if (!audio->instance) {
+ msm_rpc_close(audio->rpc_endpt);
+ audio->rpc_endpt = NULL;
+ }
+ goto err;
+ }
+ audio->instance++;
+ }
+err:
+ mutex_unlock(&audio->lock);
+ return ret;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int rc = 0;
+ int count = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_mvs_info_type *audio = &audio_mvs_info;
+ uint32_t index;
+ MM_DBG("%s\n", __func__);
+ if (audio->dl_play == 1) {
+ rc = wait_event_interruptible_timeout(audio->in_wait,
+ (audio->in_write - audio->in_read <= 3),
+ 100 * HZ);
+ if (!rc) {
+ MM_ERR("MVS: write time out\n");
+ return -ETIMEDOUT;
+ } else if (rc < 0) {
+ MM_ERR("MVS: write was interrupted\n");
+ return -ERESTARTSYS;
+ }
+ }
+ mutex_lock(&audio->in_lock);
+ if (audio->state == AUDIO_MVS_ENABLED) {
+ index = audio->in_write % MVS_MAX_Q_LEN;
+ count = frames_to_bytes(runtime, frames);
+ if (count <= MVS_MAX_VOC_PKT_SIZE) {
+ rc = copy_from_user(audio->in[index].voc_pkt, buf,
+ count);
+ } else
+ rc = -ENOMEM;
+ if (!rc) {
+ audio->in[index].len = count;
+ audio->in_write++;
+ rc = count;
+ if (audio->in_write >= 3)
+ audio->dl_play = 1;
+ } else {
+ MM_ERR("Copy from user returned %d\n", rc);
+ rc = -EFAULT;
+ }
+
+ } else {
+ MM_ERR("Write performed in invalid state %d\n",
+ audio->state);
+ rc = -EINVAL;
+ }
+ mutex_unlock(&audio->in_lock);
+ return rc;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff,
+ void __user *buf, snd_pcm_uframes_t frames)
+{
+ int rc = 0;
+ int count = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_mvs_info_type *audio = &audio_mvs_info;
+ uint32_t index = 0;
+
+ MM_DBG("%s\n", __func__);
+
+ /* Ensure the driver has been enabled. */
+ if (audio->state != AUDIO_MVS_ENABLED) {
+ MM_ERR("Read performed in invalid state %d\n", audio->state);
+ return -EPERM;
+ }
+ rc = wait_event_interruptible_timeout(audio->out_wait,
+ (audio->out_read < audio->out_write ||
+ audio->state == AUDIO_MVS_CLOSING ||
+ audio->state == AUDIO_MVS_CLOSED),
+ 100 * HZ);
+ if (!rc) {
+ MM_ERR("MVS: No UL data available\n");
+ return -ETIMEDOUT;
+ } else if (rc < 0) {
+ MM_ERR("MVS: Read was interrupted\n");
+ return -ERESTARTSYS;
+ }
+
+ mutex_lock(&audio->out_lock);
+ if (audio->state == AUDIO_MVS_CLOSING
+ || audio->state == AUDIO_MVS_CLOSED) {
+ rc = -EBUSY;
+ } else {
+ count = frames_to_bytes(runtime, frames);
+ index = audio->out_read % MVS_MAX_Q_LEN;
+ if (audio->out[index].len <= count) {
+ rc = copy_to_user(buf,
+ audio->out[index].voc_pkt,
+ audio->out[index].len);
+ if (rc == 0) {
+ rc = audio->out[index].len;
+ audio->out_read++;
+ } else {
+ MM_ERR("Copy to user %d\n", rc);
+ rc = -EFAULT;
+ }
+ } else
+ rc = -ENOMEM;
+ }
+ mutex_unlock(&audio->out_lock);
+ return rc;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ MM_DBG("%s\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int rc = 0;
+ struct audio_mvs_info_type *audio = &audio_mvs_info;
+ struct audio_mvs_release_msg release_msg;
+ MM_DBG("%s\n", __func__);
+ memset(&release_msg, 0, sizeof(release_msg));
+ mutex_lock(&audio->lock);
+
+ audio->instance--;
+ wake_up(&audio->out_wait);
+
+ if (!audio->instance) {
+ if (audio->state == AUDIO_MVS_ENABLED) {
+ audio->state = AUDIO_MVS_CLOSING;
+ /* Release MVS. */
+ release_msg.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
+ msm_rpc_setup_req(&release_msg.rpc_hdr, audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_RELEASE_PROC);
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt, &release_msg,
+ sizeof(release_msg));
+ if (rc >= 0) {
+ MM_DBG("RPC write for release done\n");
+ rc = wait_event_timeout(audio->wait,
+ (audio->rpc_status !=
+ RPC_STATUS_FAILURE), 1 * HZ);
+ if (rc != 0) {
+ MM_DBG
+ ("Wait event for release succeeded\n");
+ rc = 0;
+ kthread_stop(audio->task);
+ audio->prepare_ack = 0;
+ audio->task = NULL;
+ del_timer_sync(&audio->timer);
+ } else {
+ MM_ERR
+ ("Wait event for release failed %d\n",
+ rc);
+ }
+ } else {
+ MM_ERR("RPC write for release failed %d\n", rc);
+ }
+ }
+ audio->state = AUDIO_MVS_CLOSED;
+ msm_rpc_close(audio->rpc_endpt);
+ audio->rpc_endpt = NULL;
+ }
+
+ mutex_unlock(&audio->lock);
+
+ wake_unlock(&audio->suspend_lock);
+ wake_unlock(&audio->idle_lock);
+ /* Release the IO buffers. */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mutex_lock(&audio->in_lock);
+ audio->in_write = 0;
+ audio->in_read = 0;
+ audio->playback_enable = 0;
+ audio->dl_play = 0;
+ audio->ack_dl_count = 0;
+ memset(audio->in[0].voc_pkt, 0,
+ MVS_MAX_VOC_PKT_SIZE * MVS_MAX_Q_LEN);
+ audio->in->len = 0;
+ audio->playback_substream = NULL;
+ mutex_unlock(&audio->in_lock);
+ } else {
+ mutex_lock(&audio->out_lock);
+ audio->out_write = 0;
+ audio->out_read = 0;
+ audio->capture_enable = 0;
+ audio->ack_ul_count = 0;
+ memset(audio->out[0].voc_pkt, 0,
+ MVS_MAX_VOC_PKT_SIZE * MVS_MAX_Q_LEN);
+ audio->out->len = 0;
+ audio->capture_substream = NULL;
+ mutex_unlock(&audio->out_lock);
+ }
+ return rc;
+}
+
+static int msm_mvs_pcm_setup(struct snd_pcm_substream *substream)
+{
+ int rc = 0;
+ struct audio_mvs_acquire_msg acquire_msg;
+ struct audio_mvs_info_type *audio = &audio_mvs_info;
+ memset(&acquire_msg, 0, sizeof(acquire_msg));
+
+ /*Create an Kthread */
+ MM_DBG("ALSA MVS thread creating\n");
+ if (!IS_ERR(audio->rpc_endpt)) {
+ audio->task =
+ kthread_run(audio_mvs_thread, audio,
+ "audio_alsa_mvs_thread");
+ if (!IS_ERR(audio->task)) {
+ MM_DBG("ALSA MVS thread create succeeded\n");
+ audio->rpc_prog = MVS_PROG;
+ audio->rpc_ver = MVS_VERS;
+ /* Acquire MVS. */
+ acquire_msg.acquire_args.client_id =
+ cpu_to_be32(MVS_CLIENT_ID_VOIP);
+ acquire_msg.acquire_args.cb_func_id =
+ cpu_to_be32(MVS_CB_FUNC_ID);
+ msm_rpc_setup_req(&acquire_msg.rpc_hdr,
+ audio->rpc_prog,
+ audio->rpc_ver,
+ MVS_ACQUIRE_PROC);
+ audio->rpc_status = RPC_STATUS_FAILURE;
+ rc = msm_rpc_write(audio->rpc_endpt,
+ &acquire_msg, sizeof(acquire_msg));
+ if (rc >= 0) {
+ MM_DBG("RPC write for acquire done\n");
+
+ rc = wait_event_timeout(audio->wait,
+ (audio->rpc_status !=
+ RPC_STATUS_FAILURE),
+ 1 * HZ);
+ if (rc != 0) {
+ audio->state =
+ AUDIO_MVS_ACQUIRE;
+ rc = 0;
+ MM_DBG
+ ("MVS driver in acquire state\n");
+ } else {
+ MM_ERR
+ ("acquire Wait event failed %d\n",
+ rc);
+ rc = -EBUSY;
+ }
+ } else {
+ MM_ERR("RPC write for acquire failed %d\n",
+ rc);
+ rc = -EBUSY;
+ }
+ } else {
+ MM_ERR("ALSA MVS thread create failed\n");
+ rc = PTR_ERR(audio->task);
+ audio->task = NULL;
+ msm_rpc_close(audio->rpc_endpt);
+ audio->rpc_endpt = NULL;
+ }
+ } else {
+ MM_ERR("RPC connect is not setup with version 0x%x\n",
+ MVS_VERS);
+ rc = PTR_ERR(audio->rpc_endpt);
+ audio->rpc_endpt = NULL;
+ }
+ /*mvs mode setup */
+ if (audio->state == AUDIO_MVS_ACQUIRE)
+ rc = audio_mvs_setup_mvs(audio);
+ else
+ rc = -EBUSY;
+ return rc;
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct audio_mvs_info_type *prtd = &audio_mvs_info;
+ MM_DBG("%s\n", __func__);
+ prtd->pcm_playback_irq_pos = 0;
+ prtd->pcm_playback_buf_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->playback_enable = 1;
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct audio_mvs_info_type *prtd = &audio_mvs_info;
+ prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_capture_irq_pos = 0;
+ prtd->pcm_capture_buf_pos = 0;
+ prtd->capture_enable = 1;
+ return 0;
+}
+
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int rc = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_mvs_info_type *prtd = &audio_mvs_info;
+ unsigned long expiry = 0;
+ MM_DBG("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+
+ mutex_lock(&prtd->prepare_lock);
+ if (prtd->state == AUDIO_MVS_ENABLED)
+ goto enabled;
+ else if (prtd->state == AUDIO_MVS_PREPARING)
+ goto prepairing;
+ else if (prtd->state == AUDIO_MVS_OPENED) {
+ prtd->state = AUDIO_MVS_PREPARING;
+ rc = msm_mvs_pcm_setup(substream);
+ }
+ if (!rc) {
+ expiry = ((unsigned long)((prtd->pcm_count * 1000)
+ /(runtime->rate * runtime->channels * 2)));
+ expiry -= (expiry % 10);
+ prtd->timer.expires = jiffies + (msecs_to_jiffies(expiry));
+ prtd->expiry_delta = (msecs_to_jiffies(expiry));
+ if (prtd->expiry_delta <= 2)
+ prtd->expiry_delta = 1;
+ setup_timer(&prtd->timer, snd_pcm_mvs_timer,
+ (unsigned long)prtd);
+ prtd->ack_ul_count = 0;
+ prtd->ack_dl_count = 0;
+ add_timer(&prtd->timer);
+
+ } else {
+ MM_ERR("ALSA MVS setup is not done");
+ rc = -EPERM;
+ prtd->state = AUDIO_MVS_OPENED;
+ goto err;
+ }
+
+prepairing:
+ rc = wait_event_interruptible(prtd->prepare_wait,
+ (prtd->prepare_ack == 2));
+ if (rc < 0) {
+ MM_ERR("Wait event for prepare faild rc %d", rc);
+ rc = -EINTR;
+ prtd->state = AUDIO_MVS_OPENED;
+ goto err;
+ } else
+ MM_DBG("Wait event for prepare succeeded\n");
+
+ prtd->state = AUDIO_MVS_ENABLED;
+enabled:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rc = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ rc = msm_pcm_capture_prepare(substream);
+err:
+ mutex_unlock(&prtd->prepare_lock);
+ return rc;
+}
+
+int msm_mvs_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ MM_DBG("%s\n", __func__);
+ if (substream->pcm->device & 1) {
+ runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
+ runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t
+msm_pcm_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_mvs_info_type *audio = &audio_mvs_info;
+
+ if (audio->pcm_playback_irq_pos >= audio->pcm_size)
+ audio->pcm_playback_irq_pos = 0;
+ return bytes_to_frames(runtime, (audio->pcm_playback_irq_pos));
+}
+
+static snd_pcm_uframes_t
+msm_pcm_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_mvs_info_type *audio = &audio_mvs_info;
+
+ if (audio->pcm_capture_irq_pos >= audio->pcm_capture_size)
+ audio->pcm_capture_irq_pos = 0;
+ return bytes_to_frames(runtime, (audio->pcm_capture_irq_pos));
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t ret = 0;
+ MM_DBG("%s\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_pointer(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_pointer(substream);
+ return ret;
+}
+
+static struct snd_pcm_ops msm_mvs_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_mvs_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+
+};
+
+static int msm_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ int i, ret, offset = 0;
+ struct snd_pcm *pcm = rtd->pcm;
+
+ audio_mvs_info.mem_chunk = kmalloc(
+ 2 * MVS_MAX_VOC_PKT_SIZE * MVS_MAX_Q_LEN, GFP_KERNEL);
+ if (audio_mvs_info.mem_chunk != NULL) {
+ audio_mvs_info.in_read = 0;
+ audio_mvs_info.in_write = 0;
+ audio_mvs_info.out_read = 0;
+ audio_mvs_info.out_write = 0;
+ for (i = 0; i < MVS_MAX_Q_LEN; i++) {
+ audio_mvs_info.in[i].voc_pkt =
+ audio_mvs_info.mem_chunk + offset;
+ offset = offset + MVS_MAX_VOC_PKT_SIZE;
+ }
+ for (i = 0; i < MVS_MAX_Q_LEN; i++) {
+ audio_mvs_info.out[i].voc_pkt =
+ audio_mvs_info.mem_chunk + offset;
+ offset = offset + MVS_MAX_VOC_PKT_SIZE;
+ }
+ audio_mvs_info.playback_substream = NULL;
+ audio_mvs_info.capture_substream = NULL;
+ } else {
+ MM_ERR("MSM MVS kmalloc failed\n");
+ return -ENODEV;
+ }
+
+
+ ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 1);
+ if (ret)
+ return ret;
+ ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
+ if (ret)
+ return ret;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &msm_mvs_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &msm_mvs_pcm_ops);
+
+ return 0;
+}
+
+struct snd_soc_platform_driver msm_mvs_soc_platform = {
+ .ops = &msm_mvs_pcm_ops,
+ .pcm_new = msm_pcm_new,
+};
+EXPORT_SYMBOL(msm_mvs_soc_platform);
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_mvs_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-mvs-audio",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_mvs_soc_platform_init(void)
+{
+ memset(&audio_mvs_info, 0, sizeof(audio_mvs_info));
+ mutex_init(&audio_mvs_info.lock);
+ mutex_init(&audio_mvs_info.prepare_lock);
+ mutex_init(&audio_mvs_info.in_lock);
+ mutex_init(&audio_mvs_info.out_lock);
+ init_waitqueue_head(&audio_mvs_info.wait);
+ init_waitqueue_head(&audio_mvs_info.prepare_wait);
+ init_waitqueue_head(&audio_mvs_info.out_wait);
+ init_waitqueue_head(&audio_mvs_info.in_wait);
+ wake_lock_init(&audio_mvs_info.suspend_lock, WAKE_LOCK_SUSPEND,
+ "audio_mvs_suspend");
+ wake_lock_init(&audio_mvs_info.idle_lock, WAKE_LOCK_IDLE,
+ "audio_mvs_idle");
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_mvs_soc_platform_init);
+
+static void __exit msm_mvs_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_mvs_soc_platform_exit);
+
+MODULE_DESCRIPTION("MVS PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-hostless.c b/sound/soc/msm/msm-pcm-hostless.c
new file mode 100644
index 0000000..c61511d
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-hostless.c
@@ -0,0 +1,61 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+
+static struct snd_pcm_ops msm_pcm_hostless_ops = {};
+
+static struct snd_soc_platform_driver msm_soc_hostless_platform = {
+ .ops = &msm_pcm_hostless_ops,
+};
+
+static __devinit int msm_pcm_hostless_probe(struct platform_device *pdev)
+{
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_hostless_platform);
+}
+
+static int msm_pcm_hostless_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_hostless_driver = {
+ .driver = {
+ .name = "msm-pcm-hostless",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_hostless_probe,
+ .remove = __devexit_p(msm_pcm_hostless_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_pcm_hostless_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_hostless_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("Hostless platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-lpa.c b/sound/soc/msm/msm-pcm-lpa.c
new file mode 100644
index 0000000..10546fb
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-lpa.c
@@ -0,0 +1,464 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+
+#include "msm-pcm-q6.h"
+#include "msm-pcm-routing.h"
+
+static struct audio_locks the_locks;
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 2 * 1024 * 1024,
+/* TODO: Check on the lowest period size we can support */
+ .period_bytes_min = 128 * 1024,
+ .period_bytes_max = 512 * 1024,
+ .periods_min = 4,
+ .periods_max = 16,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_aio_write_param param;
+ struct audio_buffer *buf = prtd->audio_client->port[IN].buf;
+ int i = 0;
+
+ pr_debug("%s\n", __func__);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE: {
+ uint32_t *ptrmem = (uint32_t *)¶m;
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start))
+ break;
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+
+ param.paddr = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count);
+ param.len = prtd->pcm_count;
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.uid = (unsigned long)buf[0].phys
+ + (prtd->out_head * prtd->pcm_count);
+ for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
+ i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1) & (runtime->periods - 1);
+ break;
+ }
+ case ASM_DATA_CMDRSP_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case APR_BASIC_RSP_RESULT: {
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN: {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__, prtd->pcm_count);
+ param.paddr = (unsigned long)buf[prtd->out_head].data;
+ param.len = prtd->pcm_count;
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.uid = (unsigned long)buf[prtd->out_head].data;
+ if (q6asm_async_write(prtd->audio_client,
+ ¶m) < 0)
+ pr_err("%s:q6asm_async_write failed\n",
+ __func__);
+ else
+ prtd->out_head =
+ (prtd->out_head + 1)
+ & (runtime->periods - 1);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ if (prtd->enabled)
+ return 0;
+
+ ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
+ runtime->channels);
+ if (ret < 0)
+ pr_debug("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+ return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ pr_debug("%s\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("SNDRV_PCM_TRIGGER_START\n");
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ atomic_set(&prtd->start, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ runtime->hw = msm_pcm_hardware;
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_debug("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
+ if (ret < 0) {
+ pr_err("%s: Set IO mode failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return -EPERM;
+ pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream);
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ runtime->private_data = prtd;
+
+ return 0;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = 0;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ ret = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (ret < 0)
+ pr_err("%s: CMD_EOS failed\n", __func__);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ pr_debug("%s\n", __func__);
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ pr_debug("%s\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+ pr_debug("%s: pcm_irq_pos = %d\n", __func__, prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+ dma_mmap_coherent(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return 0;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ return -EPERM;
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed \
+ rc = %d\n", ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+ if (!buf && !buf[0].data)
+ return -ENOMEM;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n",
+ __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-lpa",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-q6.c b/sound/soc/msm/msm-pcm-q6.c
new file mode 100644
index 0000000..0535693
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-q6.c
@@ -0,0 +1,689 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+
+#include "msm-pcm-q6.h"
+#include "msm-pcm-routing.h"
+
+static struct audio_locks the_locks;
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 4096 * 8,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 4096,
+ .periods_min = 8,
+ .periods_max = 8,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static uint32_t in_frame_info[8][2];
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ uint32_t *ptrmem = (uint32_t *)payload;
+ int i = 0;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ pr_debug("%s\n", __func__);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE: {
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start))
+ break;
+ if (!prtd->mmap_flag)
+ break;
+ if (q6asm_is_cpu_buf_avail(IN,
+ prtd->audio_client,
+ &size, &idx)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+ q6asm_write(prtd->audio_client,
+ prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ }
+ break;
+ }
+ case ASM_DATA_CMDRSP_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case ASM_DATA_EVENT_READ_DONE: {
+ pr_debug("ASM_DATA_EVENT_READ_DONE\n");
+ pr_debug("token = 0x%08x\n", token);
+ for (i = 0; i < 8; i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ in_frame_info[token][0] = payload[2];
+ in_frame_info[token][1] = payload[3];
+ prtd->pcm_irq_pos += in_frame_info[token][0];
+ pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ if (atomic_read(&prtd->in_count) <= prtd->periods)
+ atomic_inc(&prtd->in_count);
+ wake_up(&the_locks.read_wait);
+ if (prtd->mmap_flag
+ && q6asm_is_cpu_buf_avail(OUT,
+ prtd->audio_client,
+ &size, &idx))
+ q6asm_read(prtd->audio_client);
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ if (!prtd->mmap_flag
+ && !atomic_read(&prtd->out_needed))
+ break;
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN:
+ if (substream->stream
+ != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ if (prtd->mmap_flag) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ } else {
+ while (atomic_read(&prtd->out_needed)) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ atomic_dec(&prtd->out_needed);
+ wake_up(&the_locks.write_wait);
+ };
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ if (prtd->enabled)
+ return 0;
+
+ ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
+ runtime->channels);
+ if (ret < 0)
+ pr_info("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret = 0;
+ int i = 0;
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+
+ if (prtd->enabled)
+ return 0;
+
+ pr_debug("Samp_rate = %d\n", prtd->samp_rate);
+ pr_debug("Channel = %d\n", prtd->channel_mode);
+ ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
+ prtd->channel_mode);
+ if (ret < 0)
+ pr_debug("%s: cmd cfg pcm was block failed", __func__);
+
+ for (i = 0; i < runtime->periods; i++)
+ q6asm_read(prtd->audio_client);
+ prtd->periods = runtime->periods;
+
+ prtd->enabled = 1;
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: Trigger start\n", __func__);
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ atomic_set(&prtd->start, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ runtime->hw = msm_pcm_hardware;
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_info("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm in open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+
+ pr_info("%s: session ID %d\n", __func__, prtd->audio_client->session);
+
+ prtd->session_id = prtd->audio_client->session;
+ msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+ prtd->session_id, substream->stream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->cmd_ack = 1;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ runtime->private_data = prtd;
+
+ return 0;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer = 0;
+ char *bufptr = NULL;
+ void *data = NULL;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ pr_debug("%s: prtd->out_count = %d\n",
+ __func__, atomic_read(&prtd->out_count));
+ ret = wait_event_timeout(the_locks.write_wait,
+ (atomic_read(&prtd->out_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_err("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+
+ if (!atomic_read(&prtd->out_count)) {
+ pr_err("%s: pcm stopped out_count 0\n", __func__);
+ return 0;
+ }
+
+ data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ if (bufptr) {
+ pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
+ __func__, fbytes, xfer, size);
+ xfer = fbytes;
+ if (copy_from_user(bufptr, buf, xfer)) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ buf += xfer;
+ fbytes -= xfer;
+ pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
+ if (atomic_read(&prtd->start)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
+ __func__, xfer);
+ ret = q6asm_write(prtd->audio_client, xfer,
+ 0, 0, NO_TIMESTAMP);
+ if (ret < 0) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ atomic_inc(&prtd->out_needed);
+ atomic_dec(&prtd->out_count);
+ }
+fail:
+ return ret;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = 0;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ ret = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (ret < 0)
+ pr_err("%s: CMD_EOS failed\n", __func__);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return 0;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer;
+ char *bufptr;
+ void *data = NULL;
+ static uint32_t idx;
+ static uint32_t size;
+ uint32_t offset = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = substream->runtime->private_data;
+
+
+ pr_debug("%s\n", __func__);
+ fbytes = frames_to_bytes(runtime, frames);
+
+ pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
+ pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
+ pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
+
+ ret = wait_event_timeout(the_locks.read_wait,
+ (atomic_read(&prtd->in_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_debug("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+ if (!atomic_read(&prtd->in_count)) {
+ pr_debug("%s: pcm stopped in_count 0\n", __func__);
+ return 0;
+ }
+ pr_debug("Checking if valid buffer is available...%08x\n",
+ (unsigned int) data);
+ data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ pr_debug("Size = %d\n", size);
+ pr_debug("fbytes = %d\n", fbytes);
+ pr_debug("idx = %d\n", idx);
+ if (bufptr) {
+ xfer = fbytes;
+ if (xfer > size)
+ xfer = size;
+ offset = in_frame_info[idx][1];
+ pr_debug("Offset value = %d\n", offset);
+ if (copy_to_user(buf, bufptr+offset, xfer)) {
+ pr_err("Failed to copy buf to user\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ fbytes -= xfer;
+ size -= xfer;
+ in_frame_info[idx][1] += xfer;
+ pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
+ __func__, fbytes, size, xfer);
+ pr_debug(" Sending next buffer to dsp\n");
+ memset(&in_frame_info[idx], 0,
+ sizeof(uint32_t) * 2);
+ atomic_dec(&prtd->in_count);
+ ret = q6asm_read(prtd->audio_client);
+ if (ret < 0) {
+ pr_err("q6asm read failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ pr_err("No valid buffer\n");
+
+ pr_debug("Returning from capture_copy... %d\n", ret);
+fail:
+ return ret;
+}
+
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = OUT;
+
+ pr_debug("%s\n", __func__);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+ msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+ SNDRV_PCM_STREAM_CAPTURE);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+
+ pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+ dma_mmap_coherent(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return 0;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed \
+ rc = %d\n", ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-dsp",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-q6.h b/sound/soc/msm/msm-pcm-q6.h
new file mode 100644
index 0000000..6a0635b
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-q6.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2009,2011 Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#ifndef _MSM_PCM_H
+#define _MSM_PCM_H
+#include <sound/apr_audio.h>
+#include <sound/q6asm.h>
+
+
+/* Support unconventional sample rates 12000, 24000 as well */
+#define USE_RATE \
+ (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+extern int copy_count;
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct buffer_rec {
+ void *data;
+ unsigned int size;
+ unsigned int read;
+ unsigned int addr;
+};
+
+struct audio_locks {
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t eos_wait;
+ wait_queue_head_t enable_wait;
+};
+
+struct msm_audio {
+ struct snd_pcm_substream *substream;
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ uint16_t source; /* Encoding source bit mask */
+
+ struct audio_client *audio_client;
+
+ uint16_t session_id;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t dsp_cnt;
+
+ int abort; /* set when error, like sample rate mismatch */
+
+ int enabled;
+ int close_ack;
+ int cmd_ack;
+ atomic_t start;
+ atomic_t out_count;
+ atomic_t in_count;
+ atomic_t out_needed;
+ int out_head;
+ int periods;
+ int mmap_flag;
+};
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
new file mode 100644
index 0000000..06f72b7
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -0,0 +1,688 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm.h>
+#include <sound/q6afe.h>
+
+#include "msm-pcm-routing.h"
+#include "qdsp6/q6voice.h"
+
+struct audio_mixer_data {
+ u32 port_id; /* AFE port ID for Rx, FE DAI ID for TX */
+ unsigned long dai_sessions; /* Rx: FE DAIs Tx: BE DAI */
+ u32 mixer_type; /* playback or capture */
+};
+
+#define INVALID_SESSION -1
+
+enum {
+ AUDIO_MIXER_PRI_I2S_RX = 0,
+ AUDIO_MIXER_SLIMBUS_0_RX,
+ AUDIO_MIXER_HDMI_RX,
+ AUDIO_MIXER_MM_UL1,
+ AUDIO_MIXER_INT_BT_SCO_RX,
+ AUDIO_MIXER_INT_FM_RX,
+ AUDIO_MIXER_MAX,
+};
+
+enum {
+ AUDIO_PORT_MIXER_SLIM_0_RX = 0,
+ AUDIO_PORT_MIXER_MAX,
+};
+
+/* Tx mixer session is stored based on BE DAI ID
+ * Need to map to actual AFE port ID since AFE port
+ * ID can get really large.
+ * The table convert DAI back to AFE port ID
+ */
+static int bedai_port_map[MSM_BACKEND_DAI_MAX] = {
+ PRIMARY_I2S_RX,
+ PRIMARY_I2S_TX,
+ SLIMBUS_0_RX,
+ SLIMBUS_0_TX,
+ HDMI_RX,
+ INT_BT_SCO_RX,
+ INT_BT_SCO_TX,
+ INT_FM_RX,
+ INT_FM_TX,
+};
+
+/* Track ASM playback & capture sessions of DAI */
+static int fe_dai_map[MSM_FRONTEND_DAI_MAX][2] = {
+ /* MULTIMEDIA1 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA2 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA3 */
+ {INVALID_SESSION, INVALID_SESSION},
+};
+
+static struct audio_mixer_data audio_mixers[AUDIO_MIXER_MAX] = {
+ /* AUDIO_MIXER_PRI_I2S_RX */
+ {PRIMARY_I2S_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* AUDIO_MIXER_SLIMBUS_0_RX */
+ {SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* AUDIO_MIXER_HDMI_RX */
+ {HDMI_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* AUDIO_MIXER_MM_UL1 */
+ {MSM_FRONTEND_DAI_MULTIMEDIA1, 0, SNDRV_PCM_STREAM_CAPTURE},
+ /* AUDIO_MIXER_INT_BT_SCO_RX */
+ {INT_BT_SCO_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* AUDIO_MIXER_INT_FM_RX */
+ {INT_FM_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+};
+static struct voice_mixer_data voice_mixers[VOICE_MIXER_MAX] = {
+ /* VOICE_MIXER_PRI_I2S_RX */
+ {VOICE_PRI_I2S_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* VOICE_MIXER_SLIMBUS_0_RX */
+ {VOICE_SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* VOICE_MIXER_PRI_I2S_TX */
+ {VOICE_PRI_I2S_TX, 0, SNDRV_PCM_STREAM_CAPTURE},
+ /* VOICE_MIXER_SLIMBUS_0_TX */
+ {VOICE_SLIMBUS_0_TX, 0, SNDRV_PCM_STREAM_CAPTURE},
+ /* VOICE_MIXER_INT_BT_SCO_RX */
+ {VOICE_INT_BT_SCO_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* VOICE_MIXER_INT_BT_SCO_TX */
+ {VOICE_INT_BT_SCO_TX, 0, SNDRV_PCM_STREAM_CAPTURE}
+};
+
+/* Reuse audio_mixer_data struct but ignore mixer type field
+ * unless there is use case for RX -> TX
+ */
+static struct audio_mixer_data audio_port_mixers[AUDIO_PORT_MIXER_MAX] = {
+ /* AUDIO_PORT_MIXER_SLIM_0_RX */
+ {SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+};
+
+void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id, int stream_type)
+{
+ int i, be_id;
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ fe_dai_map[fedai_id][0] = dspst_id;
+ for (i = 0; i < AUDIO_MIXER_MAX; i++) {
+ if ((audio_mixers[i].mixer_type == stream_type) &&
+ (test_bit(fedai_id, &audio_mixers[i].dai_sessions)))
+ /* To do: multiple devices case */
+ adm_route_session(audio_mixers[i].port_id,
+ dspst_id, 1);
+ }
+ } else {
+ fe_dai_map[fedai_id][1] = dspst_id;
+ for (i = 0; i < AUDIO_MIXER_MAX; i++) {
+ if ((audio_mixers[i].mixer_type == stream_type) &&
+ (fedai_id == audio_mixers[i].port_id)) {
+ /* To-do: Handle mixing of inputs */
+ be_id = find_next_bit(
+ &audio_mixers[i].dai_sessions,
+ MSM_BACKEND_DAI_MAX, 0);
+ if (be_id < MSM_BACKEND_DAI_MAX)
+ adm_route_session(bedai_port_map[be_id],
+ dspst_id, 1);
+ else
+ pr_err("%s: no routing\n", __func__);
+ }
+ }
+ }
+}
+
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
+{
+ int i, be_id;
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < AUDIO_MIXER_MAX; i++) {
+ if ((audio_mixers[i].mixer_type == stream_type) &&
+ (test_bit(fedai_id, &audio_mixers[i].dai_sessions))) {
+ /* To do: multiple devices case */
+ adm_route_session(audio_mixers[i].port_id,
+ fe_dai_map[fedai_id][0], 0);
+ }
+ }
+ fe_dai_map[fedai_id][0] = INVALID_SESSION;
+ } else {
+ for (i = 0; i < AUDIO_MIXER_MAX; i++) {
+ if ((audio_mixers[i].mixer_type == stream_type) &&
+ (fedai_id == audio_mixers[i].port_id)) {
+ /* To-do: Handle mixing of inputs */
+ be_id = find_next_bit(
+ &audio_mixers[i].dai_sessions,
+ MSM_BACKEND_DAI_MAX, 0);
+ if (be_id < MSM_BACKEND_DAI_MAX)
+ adm_route_session(bedai_port_map[be_id],
+ fe_dai_map[fedai_id][1], 0);
+ else
+ pr_err("%s: no routing\n", __func__);
+ }
+ }
+ fe_dai_map[fedai_id][1] = INVALID_SESSION;
+ }
+}
+
+static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
+{
+
+ pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+
+ if (set)
+ set_bit(val, &audio_mixers[reg].dai_sessions);
+ else
+ clear_bit(val, &audio_mixers[reg].dai_sessions);
+
+ if (audio_mixers[reg].mixer_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (fe_dai_map[val][0] != INVALID_SESSION)
+ adm_route_session(audio_mixers[reg].port_id,
+ fe_dai_map[val][0], set);
+ } else {
+ int fe_id = audio_mixers[reg].port_id;
+ if (fe_dai_map[fe_id][1] != INVALID_SESSION)
+ adm_route_session(bedai_port_map[val],
+ fe_dai_map[fe_id][1], set);
+ }
+}
+
+static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &audio_mixers[mc->reg].dai_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+
+ if (ucontrol->value.integer.value[0]) {
+ msm_pcm_routing_process_audio(mc->reg, mc->shift, 1);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else {
+ msm_pcm_routing_process_audio(mc->reg, mc->shift, 0);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+
+ return 1;
+}
+
+static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set)
+{
+
+ u32 port_map_id;
+
+ pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+
+ port_map_id = voice_mixers[reg].port_id;
+
+ if (set)
+ set_bit(val, &voice_mixers[reg].dai_sessions);
+ else
+ clear_bit(val, &voice_mixers[reg].dai_sessions);
+
+ if (voice_mixers[reg].mixer_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ voc_set_route_flag(RX_PATH, set);
+ if (set) {
+ voc_set_rxtx_port(bedai_port_map[port_map_id], DEV_RX);
+
+ if (voc_get_route_flag(RX_PATH) &&
+ voc_get_route_flag(TX_PATH))
+ voc_enable_cvp();
+ } else {
+ voc_disable_cvp();
+ }
+ } else {
+ voc_set_route_flag(TX_PATH, set);
+ if (set) {
+ voc_set_rxtx_port(bedai_port_map[port_map_id], DEV_TX);
+
+ if (voc_get_route_flag(RX_PATH) &&
+ voc_get_route_flag(TX_PATH))
+ voc_enable_cvp();
+ } else {
+ voc_disable_cvp();
+ }
+ }
+}
+
+static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &voice_mixers[mc->reg].dai_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (ucontrol->value.integer.value[0]) {
+ msm_pcm_routing_process_voice(mc->reg, mc->shift, 1);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else {
+ msm_pcm_routing_process_voice(mc->reg, mc->shift, 0);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+
+ return 1;
+}
+
+static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &audio_port_mixers[mc->reg].dai_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg,
+ mc->shift, ucontrol->value.integer.value[0]);
+
+ if (ucontrol->value.integer.value[0]) {
+ afe_loopback(1, audio_port_mixers[mc->reg].port_id,
+ bedai_port_map[mc->shift]);
+ set_bit(mc->shift,
+ &audio_port_mixers[mc->reg].dai_sessions);
+ } else {
+ afe_loopback(0, audio_port_mixers[mc->reg].port_id,
+ bedai_port_map[mc->shift]);
+ clear_bit(mc->shift,
+ &audio_port_mixers[mc->reg].dai_sessions);
+ }
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_PRI_I2S_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX", AUDIO_MIXER_MM_UL1,
+ MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_MIXER_MM_UL1,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", AUDIO_MIXER_MM_UL1,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", AUDIO_MIXER_MM_UL1,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", VOICE_MIXER_PRI_I2S_RX ,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", VOICE_MIXER_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", VOICE_MIXER_INT_BT_SCO_RX ,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX_Voice", VOICE_MIXER_PRI_I2S_TX,
+ MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX_Voice", VOICE_MIXER_SLIMBUS_0_TX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice", VOICE_MIXER_INT_BT_SCO_TX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX_Voip", VOICE_MIXER_PRI_I2S_TX,
+ MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX_Voip", VOICE_MIXER_SLIMBUS_0_TX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voip", VOICE_MIXER_INT_BT_SCO_TX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", AUDIO_PORT_MIXER_SLIM_0_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_PORT_MIXER_SLIM_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
+ /* Frontend AIF */
+ /* Widget name equals to Front-End DAI name<Need confirmation>,
+ * Stream name must contains substring of front-end dai name
+ */
+ SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ /* Backend AIF */
+ /* Stream name equals to backend dai link stream name
+ */
+ SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture",
+ 0, 0, 0, 0),
+
+ /* Mixer definitions */
+ SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
+ hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
+ /* Voice Mixer */
+ SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
+ ARRAY_SIZE(pri_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ slimbus_rx_voice_mixer_controls,
+ ARRAY_SIZE(slimbus_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ bt_sco_rx_voice_mixer_controls,
+ ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
+ ARRAY_SIZE(tx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voip_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls,
+ ARRAY_SIZE(tx_voip_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls,
+ ARRAY_SIZE(sbus_0_rx_port_mixer_controls)),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
+
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
+
+ {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
+ {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
+ {"HDMI", NULL, "HDMI Mixer"},
+
+ {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
+ {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
+
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MM_UL1", NULL, "MultiMedia1 Mixer"},
+
+ {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
+
+ {"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
+
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
+
+ {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+ {"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
+ {"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
+ {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
+ {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
+ {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
+ {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
+ {"VOIP_UL", NULL, "Voip_Tx Mixer"},
+ {"SLIMBUS_0_RX", NULL, "SLIM0_DL_HL"},
+ {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
+ {"INT_FM_RX", NULL, "INTFM_DL_HL"},
+ {"INTFM_UL_HL", NULL, "INT_FM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
+};
+
+static struct snd_pcm_ops msm_routing_pcm_ops = {};
+
+static unsigned int msm_routing_read(struct snd_soc_platform *platform,
+ unsigned int reg)
+{
+ dev_dbg(platform->dev, "reg %x\n", reg);
+ return 0;
+}
+
+/* Not used but frame seems to require it */
+static int msm_routing_write(struct snd_soc_platform *platform,
+ unsigned int reg, unsigned int val)
+{
+ dev_dbg(platform->dev, "reg %x val %x\n", reg, val);
+ return 0;
+}
+
+/* Not used but frame seems to require it */
+static int msm_routing_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_dapm_new_controls(&platform->dapm, msm_qdsp6_widgets,
+ ARRAY_SIZE(msm_qdsp6_widgets));
+ snd_soc_dapm_add_routes(&platform->dapm, intercon,
+ ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(&platform->dapm);
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_routing_platform = {
+ .ops = &msm_routing_pcm_ops,
+ .probe = msm_routing_probe,
+ .read = msm_routing_read,
+ .write = msm_routing_write,
+};
+
+static __devinit int msm_routing_pcm_probe(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_routing_platform);
+}
+
+static int msm_routing_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_routing_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-routing",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_routing_pcm_probe,
+ .remove = __devexit_p(msm_routing_pcm_remove),
+};
+
+static int __init msm_soc_routing_platform_init(void)
+{
+ return platform_driver_register(&msm_routing_pcm_driver);
+}
+module_init(msm_soc_routing_platform_init);
+
+static void __exit msm_soc_routing_platform_exit(void)
+{
+ platform_driver_unregister(&msm_routing_pcm_driver);
+}
+module_exit(msm_soc_routing_platform_exit);
+
+MODULE_DESCRIPTION("MSM routing platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
new file mode 100644
index 0000000..c45f95b
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -0,0 +1,81 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+#ifndef _MSM_PCM_ROUTING_H
+#define _MSM_PCM_ROUTING_H
+#include <sound/apr_audio.h>
+
+#define LPASS_BE_PRI_I2S_RX "(Backend) PRIMARY_I2S_RX"
+#define LPASS_BE_PRI_I2S_TX "(Backend) PRIMARY_I2S_TX"
+#define LPASS_BE_SLIMBUS_0_RX "(Backend) SLIMBUS_0_RX"
+#define LPASS_BE_SLIMBUS_0_TX "(Backend) SLIMBUS_0_TX"
+#define LPASS_BE_HDMI "(Backend) HDMI"
+#define LPASS_BE_INT_BT_SCO_RX "(Backend) INT_BT_SCO_RX"
+#define LPASS_BE_INT_BT_SCO_TX "(Backend) INT_BT_SCO_TX"
+#define LPASS_BE_INT_FM_RX "(Backend) INT_FM_RX"
+#define LPASS_BE_INT_FM_TX "(Backend) INT_FM_TX"
+
+enum {
+ MSM_FRONTEND_DAI_MULTIMEDIA1 = 0,
+ MSM_FRONTEND_DAI_MULTIMEDIA2,
+ MSM_FRONTEND_DAI_CS_VOICE,
+ MSM_FRONTEND_DAI_VOIP,
+ MSM_FRONTEND_DAI_MULTIMEDIA3,
+ MSM_FRONTEND_DAI_MAX,
+};
+
+enum {
+ MSM_BACKEND_DAI_PRI_I2S_RX = 0,
+ MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_BACKEND_DAI_HDMI_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_BACKEND_DAI_INT_FM_RX,
+ MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_BACKEND_DAI_MAX,
+};
+
+struct voice_mixer_data {
+ u32 port_id;
+ unsigned long dai_sessions;
+ u32 mixer_type;
+};
+
+enum {
+ VOICE_MIXER_PRI_I2S_RX = 0,
+ VOICE_MIXER_SLIMBUS_0_RX,
+ VOICE_MIXER_PRI_I2S_TX,
+ VOICE_MIXER_SLIMBUS_0_TX,
+ VOICE_MIXER_INT_BT_SCO_RX,
+ VOICE_MIXER_INT_BT_SCO_TX,
+ VOICE_MIXER_MAX,
+};
+
+enum {
+ VOICE_PRI_I2S_RX = 0,
+ VOICE_PRI_I2S_TX,
+ VOICE_SLIMBUS_0_RX,
+ VOICE_SLIMBUS_0_TX,
+ VOICE_INT_BT_SCO_RX = 5,
+ VOICE_INT_BT_SCO_TX,
+};
+
+/* dai_id: front-end ID,
+ * dspst_id: DSP audio stream ID
+ * stream_type: playback or capture
+ */
+void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id,
+ int stream_type);
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/msm-pcm-voice.c b/sound/soc/msm/msm-pcm-voice.c
new file mode 100644
index 0000000..1f581562
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-voice.c
@@ -0,0 +1,319 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include "msm-pcm-voice.h"
+#include "qdsp6/q6voice.h"
+
+static struct msm_voice voice_info;
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+
+ .info = SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+
+ .buffer_bytes_max = 4096 * 2,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 4096,
+ .periods_min = 2,
+ .periods_max = 2,
+
+ .fifo_size = 0,
+};
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+
+ if (!prtd->playback_start)
+ prtd->playback_start = 1;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+
+ if (!prtd->capture_start)
+ prtd->capture_start = 1;
+
+ return 0;
+}
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *voice = &voice_info;
+
+ mutex_lock(&voice->lock);
+
+ runtime->hw = msm_pcm_hardware;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ voice->playback_substream = substream;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ voice->capture_substream = substream;
+
+ voice->instance++;
+ pr_debug(" %s: instance: %d\n", __func__ , voice->instance);
+ runtime->private_data = voice;
+
+ mutex_unlock(&voice->lock);
+
+ return 0;
+}
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+
+ if (prtd->playback_start)
+ prtd->playback_start = 0;
+
+ prtd->playback_substream = NULL;
+
+ return 0;
+}
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+
+ if (prtd->capture_start)
+ prtd->capture_start = 0;
+ prtd->capture_substream = NULL;
+
+ return 0;
+}
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+ int ret;
+
+ mutex_lock(&prtd->lock);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+
+ prtd->instance--;
+ if (!prtd->playback_start && !prtd->capture_start) {
+ pr_debug("end voice call\n");
+ voc_end_voice_call();
+ }
+ mutex_unlock(&prtd->lock);
+
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_voice *prtd = runtime->private_data;
+
+ mutex_lock(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+
+ if (prtd->playback_start && prtd->capture_start)
+ voc_start_voice_call();
+
+ mutex_unlock(&prtd->lock);
+
+ return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+
+ pr_debug("%s, Voice\n", __func__);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int msm_voice_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2; /* Volume */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 5;
+
+ return 0;
+}
+
+static int msm_voice_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_voice_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int dir = ucontrol->value.integer.value[0];
+ int volume = ucontrol->value.integer.value[1];
+
+ pr_debug(" dir: %d, volume: %d\n", dir, volume);
+
+ voc_set_rx_vol_index(dir, volume);
+ return 0;
+}
+
+static int msm_voice_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 2;
+
+ return 0;
+}
+
+static int msm_voice_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_voice_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int dir = ucontrol->value.integer.value[0];
+ int mute = ucontrol->value.integer.value[1];
+
+ pr_debug("%s: dir=%d, mute=%d\n", __func__, dir, mute);
+
+ voc_set_tx_mute(dir, mute);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new msm_voice_controls[] = {
+ MSM_EXT("VoiceVolume", msm_voice_volume_info, msm_voice_volume_get, \
+ msm_voice_volume_put, 0),
+ MSM_EXT("VoiceMute", msm_voice_mute_info, msm_voice_mute_get, \
+ msm_voice_mute_put, 0),
+};
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+};
+
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static int msm_pcm_voice_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_add_platform_controls(platform, msm_voice_controls,
+ ARRAY_SIZE(msm_voice_controls));
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+ .probe = msm_pcm_voice_probe,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-voice",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ memset(&voice_info, 0, sizeof(voice_info));
+ mutex_init(&voice_info.lock);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("Voice PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-voice.h b/sound/soc/msm/msm-pcm-voice.h
new file mode 100644
index 0000000..2c1f5ca
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-voice.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+#ifndef _MSM_PCM_VOICE_H
+#define _MSM_PCM_VOICE_H
+#include <sound/apr_audio.h>
+
+
+struct msm_voice {
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ int instance;
+
+ struct mutex lock;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+
+ int playback_start;
+ int capture_start;
+};
+
+#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .name = xname, \
+ .info = fp_info,\
+ .get = fp_get, .put = fp_put, \
+ .private_value = addr, \
+ }
+
+#endif /*_MSM_PCM_VOICE_H*/
diff --git a/sound/soc/msm/msm-pcm-voip.c b/sound/soc/msm/msm-pcm-voip.c
new file mode 100644
index 0000000..2f2ab79
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-voip.c
@@ -0,0 +1,721 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+
+#include "msm-pcm-q6.h"
+#include "msm-pcm-routing.h"
+#include "qdsp6/q6voice.h"
+
+#define VOIP_MAX_Q_LEN 10
+#define VOIP_MAX_VOC_PKT_SIZE 320
+
+enum voip_state {
+ VOIP_STOPPED,
+ VOIP_STARTED,
+};
+
+struct voip_frame {
+ uint32_t len;
+ uint8_t voc_pkt[VOIP_MAX_VOC_PKT_SIZE];
+};
+
+struct voip_buf_node {
+ struct list_head list;
+ struct voip_frame frame;
+};
+
+struct voip_drv_info {
+ enum voip_state state;
+
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ struct list_head in_queue;
+ struct list_head free_in_queue;
+
+ struct list_head out_queue;
+ struct list_head free_out_queue;
+
+ wait_queue_head_t out_wait;
+
+ struct mutex lock;
+ struct mutex in_lock;
+ struct mutex out_lock;
+
+ spinlock_t dsp_lock;
+
+
+ uint8_t capture_start;
+ uint8_t playback_start;
+
+ uint8_t playback_instance;
+ uint8_t capture_instance;
+
+ unsigned int play_samp_rate;
+ unsigned int cap_samp_rate;
+
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_playback_irq_pos; /* IRQ position */
+ unsigned int pcm_playback_buf_pos; /* position in buffer */
+
+ unsigned int pcm_capture_size;
+ unsigned int pcm_capture_count;
+ unsigned int pcm_capture_irq_pos; /* IRQ position */
+ unsigned int pcm_capture_buf_pos; /* position in buffer */
+};
+
+static struct voip_drv_info voip_info;
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = VOIP_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN,
+ .period_bytes_min = VOIP_MAX_VOC_PKT_SIZE,
+ .period_bytes_max = VOIP_MAX_VOC_PKT_SIZE,
+ .periods_min = VOIP_MAX_Q_LEN,
+ .periods_max = VOIP_MAX_Q_LEN,
+ .fifo_size = 0,
+};
+
+
+/* sample rate supported */
+static unsigned int supported_sample_rates[] = {8000, 16000};
+
+/* capture path */
+static void voip_process_ul_pkt(uint8_t *voc_pkt,
+ uint32_t pkt_len,
+ void *private_data)
+{
+ struct voip_buf_node *buf_node = NULL;
+ struct voip_drv_info *prtd = private_data;
+ unsigned long dsp_flags;
+
+ if (prtd->capture_substream == NULL)
+ return;
+
+ /* Copy up-link packet into out_queue. */
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+
+ /* discarding UL packets till start is received */
+ if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) {
+ buf_node = list_first_entry(&prtd->free_out_queue,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+
+ buf_node->frame.len = pkt_len;
+ memcpy(&buf_node->frame.voc_pkt[0], voc_pkt,
+ buf_node->frame.len);
+
+ list_add_tail(&buf_node->list, &prtd->out_queue);
+ prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ snd_pcm_period_elapsed(prtd->capture_substream);
+ } else {
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ pr_err("UL data dropped\n");
+ }
+
+ wake_up(&prtd->out_wait);
+}
+
+/* playback path */
+static void voip_process_dl_pkt(uint8_t *voc_pkt,
+ uint32_t *pkt_len,
+ void *private_data)
+{
+ struct voip_buf_node *buf_node = NULL;
+ struct voip_drv_info *prtd = private_data;
+ unsigned long dsp_flags;
+
+
+ if (prtd->playback_substream == NULL)
+ return;
+
+ spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+
+ if (!list_empty(&prtd->in_queue) && prtd->playback_start) {
+ buf_node = list_first_entry(&prtd->in_queue,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+
+ *pkt_len = buf_node->frame.len;
+
+ memcpy(voc_pkt,
+ &buf_node->frame.voc_pkt[0],
+ buf_node->frame.len);
+
+ list_add_tail(&buf_node->list, &prtd->free_in_queue);
+
+ prtd->pcm_playback_irq_pos += prtd->pcm_count;
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ snd_pcm_period_elapsed(prtd->playback_substream);
+ } else {
+ *pkt_len = 0;
+ spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+ pr_err("DL data not available\n");
+ }
+}
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ prtd->play_samp_rate = runtime->rate;
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_playback_irq_pos = 0;
+ prtd->pcm_playback_buf_pos = 0;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+ int ret = 0;
+
+ prtd->cap_samp_rate = runtime->rate;
+ prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_capture_irq_pos = 0;
+ prtd->pcm_capture_buf_pos = 0;
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ pr_debug("%s: Trigger start\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->capture_start = 1;
+ else
+ prtd->playback_start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->playback_start = 0;
+ else
+ prtd->capture_start = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = &voip_info;
+ int ret = 0;
+
+ pr_debug("%s, VoIP\n", __func__);
+ mutex_lock(&prtd->lock);
+
+ runtime->hw = msm_pcm_hardware;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_list failed\n");
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ pr_debug("snd_pcm_hw_constraint_integer failed\n");
+ goto err;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->playback_substream = substream;
+ prtd->playback_instance++;
+ } else {
+ prtd->capture_substream = substream;
+ prtd->capture_instance++;
+ }
+ runtime->private_data = prtd;
+err:
+ mutex_unlock(&prtd->lock);
+
+ return ret;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ struct voip_buf_node *buf_node = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ int count = frames_to_bytes(runtime, frames);
+ pr_debug("%s: count = %d\n", __func__, count);
+
+ mutex_lock(&prtd->in_lock);
+
+ if (count == VOIP_MAX_VOC_PKT_SIZE) {
+ if (!list_empty(&prtd->free_in_queue)) {
+ buf_node =
+ list_first_entry(&prtd->free_in_queue,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+
+ ret = copy_from_user(&buf_node->frame.voc_pkt,
+ buf, count);
+ buf_node->frame.len = count;
+ list_add_tail(&buf_node->list, &prtd->in_queue);
+ } else
+ pr_err("%s: No free DL buffs\n", __func__);
+ } else {
+ pr_err("%s: Write count %d is not VOIP_MAX_VOC_PKT_SIZE\n",
+ __func__, count);
+ ret = -ENOMEM;
+ }
+
+ mutex_unlock(&prtd->in_lock);
+
+ return ret;
+}
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int count = 0;
+ struct voip_buf_node *buf_node = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ count = frames_to_bytes(runtime, frames);
+
+ pr_debug("%s: count = %d\n", __func__, count);
+
+ ret = wait_event_interruptible_timeout(prtd->out_wait,
+ (!list_empty(&prtd->out_queue) ||
+ prtd->state == VOIP_STOPPED),
+ 1 * HZ);
+
+ if (ret > 0) {
+ mutex_lock(&prtd->out_lock);
+
+ if (count == VOIP_MAX_VOC_PKT_SIZE) {
+ buf_node = list_first_entry(&prtd->out_queue,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+
+ ret = copy_to_user(buf,
+ &buf_node->frame.voc_pkt,
+ buf_node->frame.len);
+
+ if (ret) {
+ pr_err("%s: Copy to user retuned %d\n",
+ __func__, ret);
+ ret = -EFAULT;
+ }
+ list_add_tail(&buf_node->list,
+ &prtd->free_out_queue);
+ } else {
+ pr_err("%s: Read count %d < VOIP_MAX_VOC_PKT_SIZE\n",
+ __func__, count);
+ ret = -ENOMEM;
+ }
+
+ mutex_unlock(&prtd->out_lock);
+
+ } else if (ret == 0) {
+ pr_err("%s: No UL data available\n", __func__);
+ ret = -ETIMEDOUT;
+ } else {
+ pr_err("%s: Read was interrupted\n", __func__);
+ ret = -ERESTARTSYS;
+ }
+ return ret;
+}
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct list_head *ptr = NULL;
+ struct list_head *next = NULL;
+ struct voip_buf_node *buf_node = NULL;
+ struct snd_dma_buffer *p_dma_buf, *c_dma_buf;
+ struct snd_pcm_substream *p_substream, *c_substream;
+ struct snd_pcm_runtime *runtime;
+ struct voip_drv_info *prtd;
+
+ if (substream == NULL) {
+ pr_err("substream is NULL\n");
+ return -EINVAL;
+ }
+ runtime = substream->runtime;
+ prtd = runtime->private_data;
+
+ wake_up(&prtd->out_wait);
+
+ mutex_lock(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ prtd->playback_instance--;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ prtd->capture_instance--;
+
+ if (!prtd->playback_instance && !prtd->capture_instance) {
+ if (prtd->state == VOIP_STARTED) {
+ prtd->state = VOIP_STOPPED;
+ voc_end_voice_call();
+ voc_set_voc_path_full(0);
+ voc_register_mvs_cb(NULL, NULL, prtd);
+ }
+ /* release all buffer */
+ /* release in_queue and free_in_queue */
+ pr_debug("release all buffer\n");
+ p_substream = prtd->playback_substream;
+ if (p_substream == NULL) {
+ pr_debug("p_substream is NULL\n");
+ goto capt;
+ }
+ p_dma_buf = &p_substream->dma_buffer;
+ if (p_dma_buf == NULL) {
+ pr_debug("p_dma_buf is NULL\n");
+ goto capt;
+ }
+ if (p_dma_buf->area != NULL) {
+ mutex_lock(&prtd->in_lock);
+ list_for_each_safe(ptr, next, &prtd->in_queue) {
+ buf_node = list_entry(ptr,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ }
+ list_for_each_safe(ptr, next, &prtd->free_in_queue) {
+ buf_node = list_entry(ptr,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ }
+ dma_free_coherent(p_substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max, p_dma_buf->area,
+ p_dma_buf->addr);
+ p_dma_buf->area = NULL;
+ mutex_unlock(&prtd->in_lock);
+ }
+ /* release out_queue and free_out_queue */
+capt: c_substream = prtd->capture_substream;
+ if (c_substream == NULL) {
+ pr_debug("c_substream is NULL\n");
+ goto done;
+ }
+ c_dma_buf = &c_substream->dma_buffer;
+ if (c_substream == NULL) {
+ pr_debug("c_dma_buf is NULL.\n");
+ goto done;
+ }
+ if (c_dma_buf->area != NULL) {
+ mutex_lock(&prtd->out_lock);
+ list_for_each_safe(ptr, next, &prtd->out_queue) {
+ buf_node = list_entry(ptr,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ }
+ list_for_each_safe(ptr, next, &prtd->free_out_queue) {
+ buf_node = list_entry(ptr,
+ struct voip_buf_node, list);
+ list_del(&buf_node->list);
+ }
+ dma_free_coherent(c_substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max, c_dma_buf->area,
+ c_dma_buf->addr);
+ c_dma_buf->area = NULL;
+ mutex_unlock(&prtd->out_lock);
+ }
+done:
+ prtd->capture_substream = NULL;
+ prtd->playback_substream = NULL;
+ }
+ mutex_unlock(&prtd->lock);
+
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ mutex_lock(&prtd->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+
+ if (prtd->playback_instance && prtd->capture_instance
+ && (prtd->state != VOIP_STARTED)) {
+
+ voc_set_voc_path_full(1);
+
+ if ((prtd->play_samp_rate == 8000) &&
+ (prtd->cap_samp_rate == 8000))
+ voc_config_vocoder(VSS_MEDIA_ID_PCM_NB,
+ 0, VSS_NETWORK_ID_VOIP_NB);
+ else if ((prtd->play_samp_rate == 16000) &&
+ (prtd->cap_samp_rate == 16000))
+ voc_config_vocoder(VSS_MEDIA_ID_PCM_WB,
+ 0, VSS_NETWORK_ID_VOIP_WB);
+ else {
+ pr_err(" sample rate are not set properly\n");
+ goto err;
+ }
+ voc_register_mvs_cb(voip_process_ul_pkt,
+ voip_process_dl_pkt, prtd);
+ voc_start_voice_call();
+
+ prtd->state = VOIP_STARTED;
+ }
+err:
+ mutex_unlock(&prtd->lock);
+
+ return ret;
+}
+
+static snd_pcm_uframes_t
+msm_pcm_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ if (prtd->pcm_playback_irq_pos >= prtd->pcm_size)
+ prtd->pcm_playback_irq_pos = 0;
+ return bytes_to_frames(runtime, (prtd->pcm_playback_irq_pos));
+}
+
+static snd_pcm_uframes_t
+msm_pcm_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct voip_drv_info *prtd = runtime->private_data;
+
+ if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size)
+ prtd->pcm_capture_irq_pos = 0;
+ return bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos));
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t ret = 0;
+ pr_debug("%s\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_pointer(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_pointer(substream);
+ return ret;
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ pr_debug("%s\n", __func__);
+ dma_mmap_coherent(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return 0;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct voip_buf_node *buf_node = NULL;
+ int i = 0, offset = 0;
+
+ pr_debug("%s: voip\n", __func__);
+
+ mutex_lock(&voip_info.lock);
+
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+
+ dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev,
+ runtime->hw.buffer_bytes_max,
+ &dma_buf->addr, GFP_KERNEL);
+ if (!dma_buf->area) {
+ pr_err("%s:MSM VOIP dma_alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
+ buf_node = (void *)dma_buf->area + offset;
+
+ mutex_lock(&voip_info.in_lock);
+ list_add_tail(&buf_node->list,
+ &voip_info.free_in_queue);
+ mutex_unlock(&voip_info.in_lock);
+ offset = offset + sizeof(struct voip_buf_node);
+ }
+ } else {
+ for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
+ buf_node = (void *) dma_buf->area + offset;
+ mutex_lock(&voip_info.out_lock);
+ list_add_tail(&buf_node->list,
+ &voip_info.free_out_queue);
+ mutex_unlock(&voip_info.out_lock);
+ offset = offset + sizeof(struct voip_buf_node);
+ }
+ }
+
+ mutex_unlock(&voip_info.lock);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ int ret = 0;
+
+ pr_debug("msm_asoc_pcm_new\n");
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_asoc_pcm_new,
+};
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-voip-dsp",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ memset(&voip_info, 0, sizeof(voip_info));
+
+ mutex_init(&voip_info.lock);
+ mutex_init(&voip_info.in_lock);
+ mutex_init(&voip_info.out_lock);
+
+ spin_lock_init(&voip_info.dsp_lock);
+
+ init_waitqueue_head(&voip_info.out_wait);
+
+ INIT_LIST_HEAD(&voip_info.in_queue);
+ INIT_LIST_HEAD(&voip_info.free_in_queue);
+ INIT_LIST_HEAD(&voip_info.out_queue);
+ INIT_LIST_HEAD(&voip_info.free_out_queue);
+
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm.c b/sound/soc/msm/msm-pcm.c
new file mode 100644
index 0000000..bea528f
--- /dev/null
+++ b/sound/soc/msm/msm-pcm.c
@@ -0,0 +1,645 @@
+/* sound/soc/msm/msm-pcm.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include "msm-pcm.h"
+
+#define MAX_DATA_SIZE 496
+#define AUDPP_ALSA_DECODER (-1)
+
+#define DB_TABLE_INDEX (50)
+
+#define audio_send_queue_recbs(prtd, cmd, len) \
+ msm_adsp_write(prtd->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len)
+#define audio_send_queue_rec(prtd, cmd, len) \
+ msm_adsp_write(prtd->audrec, QDSP_uPAudRecCmdQueue, cmd, len)
+
+int intcnt;
+static int audio_dsp_send_buffer(struct msm_audio *prtd,
+ unsigned idx, unsigned len);
+
+struct audio_frame {
+ uint16_t count_low;
+ uint16_t count_high;
+ uint16_t bytes;
+ uint16_t unknown;
+ unsigned char samples[];
+} __attribute__ ((packed));
+
+/* Table contains dB to raw value mapping */
+static const unsigned decoder_db_table[] = {
+
+ 31 , /* -50 dB */
+ 35 , 39 , 44 , 50 , 56 ,
+ 63 , 70 , 79 , 89 , 99 ,
+ 112 , 125 , 141 , 158 , 177 ,
+ 199 , 223 , 251 , 281 , 316 ,
+ 354 , 398 , 446 , 501 , 562 ,
+ 630 , 707 , 794 , 891 , 999 ,
+ 1122 , 1258 , 1412 , 1584 , 1778 ,
+ 1995 , 2238 , 2511 , 2818 , 3162 ,
+ 3548 , 3981 , 4466 , 5011 , 5623 ,
+ 6309 , 7079 , 7943 , 8912 , 10000 ,
+ 11220 , 12589 , 14125 , 15848 , 17782 ,
+ 19952 , 22387 , 25118 , 28183 , 31622 ,
+ 35481 , 39810 , 44668 , 50118 , 56234 ,
+ 63095 , 70794 , 79432 , 89125 , 100000 ,
+ 112201 , 125892 , 141253 , 158489 , 177827 ,
+ 199526 , 223872 , 251188 , 281838 , 316227 ,
+ 354813 , 398107 , 446683 , 501187 , 562341 ,
+ 630957 , 707945 , 794328 , 891250 , 1000000 ,
+ 1122018 , 1258925 , 1412537 , 1584893 , 1778279 ,
+ 1995262 , 2238721 , 2511886 , 2818382 , 3162277 ,
+ 3548133 /* 51 dB */
+
+};
+
+static unsigned compute_db_raw(int db)
+{
+ unsigned reg_val = 0; /* Computed result for correspondent db */
+ /* Check if the given db is out of range */
+ if (db <= MIN_DB)
+ return 0;
+ else if (db > MAX_DB)
+ db = MAX_DB; /* If db is too high then set to max */
+ reg_val = decoder_db_table[DB_TABLE_INDEX+db];
+ return reg_val;
+}
+
+int msm_audio_volume_update(unsigned id,
+ int volume, int pan)
+{
+ unsigned vol_raw;
+
+ vol_raw = compute_db_raw(volume);
+ printk(KERN_INFO "volume: %8x vol_raw: %8x \n", volume, vol_raw);
+ return audpp_set_volume_and_pan(id, vol_raw, pan);
+}
+EXPORT_SYMBOL(msm_audio_volume_update);
+
+void alsa_dsp_event(void *data, unsigned id, uint16_t *msg)
+{
+ struct msm_audio *prtd = data;
+ struct buffer *frame;
+ unsigned long flag;
+
+ switch (id) {
+ case AUDPP_MSG_STATUS_MSG:
+ break;
+ case AUDPP_MSG_SPA_BANDS:
+ break;
+ case AUDPP_MSG_HOST_PCM_INTF_MSG:{
+ unsigned id = msg[2];
+ unsigned idx = msg[3] - 1;
+ if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
+ printk(KERN_ERR "bogus id\n");
+ break;
+ }
+ if (idx > 1) {
+ printk(KERN_ERR "bogus buffer idx\n");
+ break;
+ }
+ /* Update with actual sent buffer size */
+ if (prtd->out[idx].used != BUF_INVALID_LEN)
+ prtd->pcm_irq_pos += prtd->out[idx].used;
+
+ if (prtd->pcm_irq_pos > prtd->pcm_size)
+ prtd->pcm_irq_pos = prtd->pcm_count;
+
+ if (prtd->ops->playback)
+ prtd->ops->playback(prtd);
+
+ spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
+ if (prtd->running) {
+ prtd->out[idx].used = 0;
+ frame = prtd->out + prtd->out_tail;
+ if (frame->used) {
+ audio_dsp_send_buffer(prtd,
+ prtd->out_tail,
+ frame->used);
+ prtd->out_tail ^= 1;
+ } else {
+ prtd->out_needed++;
+ }
+ wake_up(&the_locks.write_wait);
+ }
+ spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
+ break;
+ }
+ case AUDPP_MSG_PCMDMAMISSED:
+ pr_info("alsa_dsp_event: PCMDMAMISSED %d\n", msg[0]);
+ prtd->eos_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ prtd->out_needed = 0;
+ prtd->running = 1;
+ audio_dsp_out_enable(prtd, 1);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ prtd->running = 0;
+ } else {
+ printk(KERN_ERR "alsa_dsp_event:CFG_MSG=%d\n", msg[0]);
+ }
+ break;
+ case EVENT_MSG_ID:
+ printk(KERN_INFO"alsa_dsp_event: arm9 event\n");
+ break;
+ default:
+ printk(KERN_ERR "alsa_dsp_event: UNKNOWN (%d)\n", id);
+ }
+}
+
+void alsa_audpre_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ uint16_t msg[MAX_DATA_SIZE/2];
+
+ if (len > MAX_DATA_SIZE) {
+ printk(KERN_ERR"audpre: event too large(%d bytes)\n", len);
+ return;
+ }
+ getevent(msg, len);
+
+ switch (id) {
+ case AUDPREPROC_MSG_CMD_CFG_DONE_MSG:
+ break;
+ case AUDPREPROC_MSG_ERROR_MSG_ID:
+ printk(KERN_ERR "audpre: err_index %d\n", msg[0]);
+ break;
+ case EVENT_MSG_ID:
+ printk(KERN_INFO"audpre: arm9 event\n");
+ break;
+ default:
+ printk(KERN_ERR "audpre: unknown event %d\n", id);
+ }
+}
+
+void audrec_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct msm_audio *prtd = data;
+ unsigned long flag;
+ uint16_t msg[MAX_DATA_SIZE/2];
+
+ if (len > MAX_DATA_SIZE) {
+ printk(KERN_ERR"audrec: event/msg too large(%d bytes)\n", len);
+ return;
+ }
+ getevent(msg, len);
+
+ switch (id) {
+ case AUDREC_MSG_CMD_CFG_DONE_MSG:
+ if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE) {
+ if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_ENA)
+ audrec_encoder_config(prtd);
+ else
+ prtd->running = 0;
+ }
+ break;
+ case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG:{
+ prtd->running = 1;
+ break;
+ }
+ case AUDREC_MSG_FATAL_ERR_MSG:
+ printk(KERN_ERR "audrec: ERROR %x\n", msg[0]);
+ break;
+ case AUDREC_MSG_PACKET_READY_MSG:
+ alsa_get_dsp_frames(prtd);
+ ++intcnt;
+ if (prtd->channel_mode == 1) {
+ spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+ spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
+
+ if (prtd->ops->capture)
+ prtd->ops->capture(prtd);
+ } else if ((prtd->channel_mode == 0) && (intcnt % 2 == 0)) {
+ spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+ spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
+ if (prtd->ops->capture)
+ prtd->ops->capture(prtd);
+ }
+ break;
+ case EVENT_MSG_ID:
+ printk(KERN_INFO"audrec: arm9 event\n");
+ break;
+ default:
+ printk(KERN_ERR "audrec: unknown event %d\n", id);
+ }
+}
+
+struct msm_adsp_ops aud_pre_adsp_ops = {
+ .event = alsa_audpre_dsp_event,
+};
+
+struct msm_adsp_ops aud_rec_adsp_ops = {
+ .event = audrec_dsp_event,
+};
+
+int alsa_adsp_configure(struct msm_audio *prtd)
+{
+ int ret, i;
+
+ if (prtd->dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->data = prtd->playback_substream->dma_buffer.area;
+ prtd->phys = prtd->playback_substream->dma_buffer.addr;
+ }
+ if (prtd->dir == SNDRV_PCM_STREAM_CAPTURE) {
+ prtd->data = prtd->capture_substream->dma_buffer.area;
+ prtd->phys = prtd->capture_substream->dma_buffer.addr;
+ }
+ if (!prtd->data) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ ret = audmgr_open(&prtd->audmgr);
+ if (ret)
+ goto err2;
+ if (prtd->dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->out_buffer_size = PLAYBACK_DMASZ;
+ prtd->out_sample_rate = 44100;
+ prtd->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+ prtd->out_weight = 100;
+
+ prtd->out[0].data = prtd->data + 0;
+ prtd->out[0].addr = prtd->phys + 0;
+ prtd->out[0].size = BUFSZ;
+ prtd->out[1].data = prtd->data + BUFSZ;
+ prtd->out[1].addr = prtd->phys + BUFSZ;
+ prtd->out[1].size = BUFSZ;
+ }
+ if (prtd->dir == SNDRV_PCM_STREAM_CAPTURE) {
+ prtd->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_44100;
+ prtd->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_44100;
+ prtd->channel_mode = AUDREC_CMD_STEREO_MODE_STEREO;
+ prtd->buffer_size = STEREO_DATA_SIZE;
+ prtd->type = AUDREC_CMD_TYPE_0_INDEX_WAV;
+ prtd->tx_agc_cfg.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS;
+ prtd->ns_cfg.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS;
+ prtd->iir_cfg.cmd_id =
+ AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
+
+ ret = msm_adsp_get("AUDPREPROCTASK",
+ &prtd->audpre, &aud_pre_adsp_ops, prtd);
+ if (ret)
+ goto err3;
+ ret = msm_adsp_get("AUDRECTASK",
+ &prtd->audrec, &aud_rec_adsp_ops, prtd);
+ if (ret) {
+ msm_adsp_put(prtd->audpre);
+ goto err3;
+ }
+ prtd->dsp_cnt = 0;
+ prtd->in_head = 0;
+ prtd->in_tail = 0;
+ prtd->in_count = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ prtd->in[i].size = 0;
+ prtd->in[i].read = 0;
+ }
+ }
+
+ return 0;
+
+err3:
+ audmgr_close(&prtd->audmgr);
+
+err2:
+ prtd->data = NULL;
+err1:
+ return ret;
+}
+EXPORT_SYMBOL(alsa_adsp_configure);
+
+int alsa_audio_configure(struct msm_audio *prtd)
+{
+ struct audmgr_config cfg;
+ int rc;
+
+ if (prtd->enabled)
+ return 0;
+
+ /* refuse to start if we're not ready with first buffer */
+ if (!prtd->out[0].used)
+ return -EIO;
+
+ cfg.tx_rate = 0;
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+ cfg.def_method = RPC_AUD_DEF_METHOD_HOST_PCM;
+ cfg.codec = RPC_AUD_DEF_CODEC_PCM;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+ rc = audmgr_enable(&prtd->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (audpp_enable(AUDPP_ALSA_DECODER, alsa_dsp_event, prtd)) {
+ printk(KERN_ERR "audio: audpp_enable() failed\n");
+ audmgr_disable(&prtd->audmgr);
+ return -ENODEV;
+ }
+
+ prtd->enabled = 1;
+ return 0;
+}
+EXPORT_SYMBOL(alsa_audio_configure);
+
+ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ unsigned long flag;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int rc = 0;
+
+ mutex_lock(&the_locks.write_lock);
+ while (count > 0) {
+ frame = prtd->out + prtd->out_head;
+ rc = wait_event_interruptible(the_locks.write_wait,
+ (frame->used == 0)
+ || (prtd->stopped));
+ if (rc < 0)
+ break;
+ if (prtd->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+ xfer = count > frame->size ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+ frame->used = xfer;
+ prtd->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
+ frame = prtd->out + prtd->out_tail;
+ if (frame->used && prtd->out_needed) {
+ audio_dsp_send_buffer(prtd, prtd->out_tail,
+ frame->used);
+ prtd->out_tail ^= 1;
+ prtd->out_needed--;
+ }
+ spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
+ }
+ mutex_unlock(&the_locks.write_lock);
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+EXPORT_SYMBOL(alsa_send_buffer);
+
+int alsa_audio_disable(struct msm_audio *prtd)
+{
+ if (prtd->enabled) {
+ mutex_lock(&the_locks.lock);
+ prtd->enabled = 0;
+ audio_dsp_out_enable(prtd, 0);
+ wake_up(&the_locks.write_wait);
+ audpp_disable(AUDPP_ALSA_DECODER, prtd);
+ audmgr_disable(&prtd->audmgr);
+ prtd->out_needed = 0;
+ mutex_unlock(&the_locks.lock);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(alsa_audio_disable);
+
+int alsa_audrec_disable(struct msm_audio *prtd)
+{
+ if (prtd->enabled) {
+ mutex_lock(&the_locks.lock);
+ prtd->enabled = 0;
+ alsa_rec_dsp_enable(prtd, 0);
+ wake_up(&the_locks.read_wait);
+ msm_adsp_disable(prtd->audpre);
+ msm_adsp_disable(prtd->audrec);
+ audmgr_disable(&prtd->audmgr);
+ prtd->out_needed = 0;
+ prtd->opened = 0;
+ mutex_unlock(&the_locks.lock);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(alsa_audrec_disable);
+
+static int audio_dsp_read_buffer(struct msm_audio *prtd, uint32_t read_cnt)
+{
+ audrec_cmd_packet_ext_ptr cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR;
+ /* Both WAV and AAC use AUDREC_CMD_TYPE_0 */
+ cmd.type = AUDREC_CMD_TYPE_0;
+ cmd.curr_rec_count_msw = read_cnt >> 16;
+ cmd.curr_rec_count_lsw = read_cnt;
+
+ return audio_send_queue_recbs(prtd, &cmd, sizeof(cmd));
+}
+
+int audrec_encoder_config(struct msm_audio *prtd)
+{
+ audrec_cmd_arec0param_cfg cmd;
+ uint16_t *data = (void *)prtd->data;
+ unsigned n;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_AREC0PARAM_CFG;
+ cmd.ptr_to_extpkt_buffer_msw = prtd->phys >> 16;
+ cmd.ptr_to_extpkt_buffer_lsw = prtd->phys;
+ cmd.buf_len = FRAME_NUM; /* Both WAV and AAC use 8 frames */
+ cmd.samp_rate_index = prtd->samp_rate_index;
+ /* 0 for mono, 1 for stereo */
+ cmd.stereo_mode = prtd->channel_mode;
+ cmd.rec_quality = 0x1C00;
+
+ /* prepare buffer pointers:
+ * Mono: 1024 samples + 4 halfword header
+ * Stereo: 2048 samples + 4 halfword header
+ */
+
+ for (n = 0; n < FRAME_NUM; n++) {
+ prtd->in[n].data = data + 4;
+ data += (4 + (prtd->channel_mode ? 2048 : 1024));
+ }
+
+ return audio_send_queue_rec(prtd, &cmd, sizeof(cmd));
+}
+
+int audio_dsp_out_enable(struct msm_audio *prtd, int yes)
+{
+ audpp_cmd_pcm_intf cmd;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_PCM_INTF_2;
+ cmd.object_num = AUDPP_CMD_PCM_INTF_OBJECT_NUM;
+ cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V;
+ cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+
+ if (yes) {
+ cmd.write_buf1LSW = prtd->out[0].addr;
+ cmd.write_buf1MSW = prtd->out[0].addr >> 16;
+ cmd.write_buf1_len = 0;
+ cmd.write_buf2LSW = prtd->out[1].addr;
+ cmd.write_buf2MSW = prtd->out[1].addr >> 16;
+ cmd.write_buf2_len = prtd->out[1].used;
+ cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V;
+ cmd.weight_decoder_to_rx = prtd->out_weight;
+ cmd.weight_arm_to_rx = 1;
+ cmd.partition_number_arm_to_dsp = 0;
+ cmd.sample_rate = prtd->out_sample_rate;
+ cmd.channel_mode = prtd->out_channel_mode;
+ }
+ return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+int alsa_buffer_read(struct msm_audio *prtd, void __user *buf,
+ size_t count, loff_t *pos)
+{
+ unsigned long flag;
+ void *data;
+ uint32_t index;
+ uint32_t size;
+ int rc = 0;
+
+ mutex_lock(&the_locks.read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(the_locks.read_wait,
+ (prtd->in_count > 0)
+ || prtd->stopped);
+ if (rc < 0)
+ break;
+
+ if (prtd->stopped) {
+ rc = -EBUSY;
+ break;
+ }
+
+ index = prtd->in_tail;
+ data = (uint8_t *) prtd->in[index].data;
+ size = prtd->in[index].size;
+ if (count >= size) {
+ if (copy_to_user(buf, data, size)) {
+ rc = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
+ if (index != prtd->in_tail) {
+ /* overrun: data is invalid, we need to retry */
+ spin_unlock_irqrestore(&the_locks.read_dsp_lock,
+ flag);
+ continue;
+ }
+ prtd->in[index].size = 0;
+ prtd->in_tail = (prtd->in_tail + 1) & (FRAME_NUM - 1);
+ prtd->in_count--;
+ spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
+ count -= size;
+ buf += size;
+ } else {
+ break;
+ }
+ }
+ mutex_unlock(&the_locks.read_lock);
+ return rc;
+}
+EXPORT_SYMBOL(alsa_buffer_read);
+
+static int audio_dsp_send_buffer(struct msm_audio *prtd,
+ unsigned idx, unsigned len)
+{
+ audpp_cmd_pcm_intf_send_buffer cmd;
+ cmd.cmd_id = AUDPP_CMD_PCM_INTF_2;
+ cmd.host_pcm_object = AUDPP_CMD_PCM_INTF_OBJECT_NUM;
+ cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V;
+ cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+ cmd.dsp_to_arm_buf_id = 0;
+ cmd.arm_to_dsp_buf_id = idx + 1;
+ cmd.arm_to_dsp_buf_len = len;
+ return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+int alsa_rec_dsp_enable(struct msm_audio *prtd, int enable)
+{
+ audrec_cmd_cfg cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_CFG;
+ cmd.type_0 = enable ? AUDREC_CMD_TYPE_0_ENA : AUDREC_CMD_TYPE_0_DIS;
+ cmd.type_0 |= (AUDREC_CMD_TYPE_0_UPDATE | prtd->type);
+ cmd.type_1 = 0;
+
+ return audio_send_queue_rec(prtd, &cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(alsa_rec_dsp_enable);
+
+void alsa_get_dsp_frames(struct msm_audio *prtd)
+{
+ struct audio_frame *frame;
+ uint32_t index = 0;
+ unsigned long flag;
+
+ if (prtd->type == AUDREC_CMD_TYPE_0_INDEX_WAV) {
+ index = prtd->in_head;
+
+ frame =
+ (void *)(((char *)prtd->in[index].data) - sizeof(*frame));
+
+ spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
+ prtd->in[index].size = frame->bytes;
+
+ prtd->in_head = (prtd->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (prtd->in_head == prtd->in_tail)
+ prtd->in_tail = (prtd->in_tail + 1) & (FRAME_NUM - 1);
+ else
+ prtd->in_count++;
+
+ audio_dsp_read_buffer(prtd, prtd->dsp_cnt++);
+ spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
+
+ wake_up(&the_locks.read_wait);
+ } else {
+ /* TODO AAC not supported yet. */
+ }
+}
+EXPORT_SYMBOL(alsa_get_dsp_frames);
diff --git a/sound/soc/msm/msm-pcm.h b/sound/soc/msm/msm-pcm.h
new file mode 100644
index 0000000..e7ddd30
--- /dev/null
+++ b/sound/soc/msm/msm-pcm.h
@@ -0,0 +1,200 @@
+/* sound/soc/msm/msm-pcm.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#ifndef _MSM_PCM_H
+#define _MSM_PCM_H
+
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audreccmdi.h>
+#include <mach/qdsp5/qdsp5audrecmsg.h>
+#include <mach/qdsp5/qdsp5audpreproccmdi.h>
+#include <mach/qdsp5/qdsp5audpreprocmsg.h>
+
+#include <../arch/arm/mach-msm/qdsp5/adsp.h>
+#include <../arch/arm/mach-msm/qdsp5/audmgr.h>
+
+
+#define FRAME_NUM (8)
+#define FRAME_SIZE (2052 * 2)
+#define MONO_DATA_SIZE (2048)
+#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2)
+#define CAPTURE_DMASZ (FRAME_SIZE * FRAME_NUM)
+
+#define BUFSZ (960 * 5)
+#define PLAYBACK_DMASZ (BUFSZ * 2)
+
+#define MSM_PLAYBACK_DEFAULT_VOLUME 0 /* 0dB */
+#define MSM_PLAYBACK_DEFAULT_PAN 0
+
+#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#define USE_CHANNELS_MIN 1
+#define USE_CHANNELS_MAX 2
+/* Support unconventional sample rates 12000, 24000 as well */
+#define USE_RATE \
+ (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+#define USE_RATE_MIN 8000
+#define USE_RATE_MAX 48000
+#define MAX_BUFFER_PLAYBACK_SIZE \
+ (4800*4)
+/* 2048 frames (Mono), 1024 frames (Stereo) */
+#define CAPTURE_SIZE 4096
+#define MAX_BUFFER_CAPTURE_SIZE (4096*4)
+#define MAX_PERIOD_SIZE BUFSZ
+#define USE_PERIODS_MAX 1024
+#define USE_PERIODS_MIN 1
+
+
+#define MAX_DB (16)
+#define MIN_DB (-50)
+#define PCMPLAYBACK_DECODERID 5
+
+/* 0xFFFFFFFF Indicates not to be used for audio data copy */
+#define BUF_INVALID_LEN 0xFFFFFFFF
+
+extern int copy_count;
+extern int intcnt;
+
+struct msm_volume {
+ bool update;
+ int volume; /* Volume parameter, in dB Scale */
+ int pan;
+};
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct buffer_rec {
+ void *data;
+ unsigned int size;
+ unsigned int read;
+ unsigned int addr;
+};
+
+struct audio_locks {
+ struct mutex lock;
+ struct mutex write_lock;
+ struct mutex read_lock;
+ spinlock_t read_dsp_lock;
+ spinlock_t write_dsp_lock;
+ spinlock_t mixer_lock;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t eos_wait;
+};
+
+extern struct audio_locks the_locks;
+
+struct msm_audio_event_callbacks {
+ /* event is called from interrupt context when a message
+ * arrives from the DSP.
+ */
+ void (*playback)(void *);
+ void (*capture)(void *);
+};
+
+
+struct msm_audio {
+ struct buffer out[2];
+ struct buffer_rec in[8];
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ atomic_t out_bytes;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ uint32_t out_weight;
+ uint32_t out_buffer_size;
+
+ struct audmgr audmgr;
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ unsigned int pcm_buf_pos; /* position in buffer */
+
+ struct msm_adsp_module *audpre;
+ struct msm_adsp_module *audrec;
+
+ /* configuration to use on next enable */
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */
+ uint32_t type; /* 0 for PCM ,1 for AAC */
+ uint32_t dsp_cnt;
+ uint32_t in_head; /* next buffer dsp will write */
+ uint32_t in_tail; /* next buffer read() will read */
+ uint32_t in_count; /* number of buffers available to read() */
+
+ unsigned short samp_rate_index;
+
+ /* audpre settings */
+ audpreproc_cmd_cfg_agc_params tx_agc_cfg;
+ audpreproc_cmd_cfg_ns_params ns_cfg;
+ /* For different sample rate, the coeff might be different. *
+ * All the coeff should be passed from user space */
+ audpreproc_cmd_cfg_iir_tuning_filter_params iir_cfg;
+
+ struct msm_audio_event_callbacks *ops;
+
+ int dir;
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int eos_ack;
+};
+
+
+
+/* platform data */
+extern int audio_dsp_out_enable(struct msm_audio *prtd, int yes);
+extern struct snd_soc_platform_driver msm_soc_platform;
+
+int audrec_encoder_config(struct msm_audio *prtd);
+extern void alsa_get_dsp_frames(struct msm_audio *prtd);
+extern int alsa_rec_dsp_enable(struct msm_audio *prtd, int enable);
+extern int alsa_audrec_disable(struct msm_audio *prtd);
+extern int alsa_audio_configure(struct msm_audio *prtd);
+extern int alsa_audio_disable(struct msm_audio *prtd);
+extern int alsa_adsp_configure(struct msm_audio *prtd);
+extern int alsa_buffer_read(struct msm_audio *prtd, void __user *buf,
+ size_t count, loff_t *pos);
+ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
+ size_t count, loff_t *pos);
+int msm_audio_volume_update(unsigned id,
+ int volume, int pan);
+extern struct audio_locks the_locks;
+extern struct msm_volume msm_vol_ctl;
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/msm-voip.c b/sound/soc/msm/msm-voip.c
new file mode 100644
index 0000000..082c840
--- /dev/null
+++ b/sound/soc/msm/msm-voip.c
@@ -0,0 +1,610 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/wakelock.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6asm.h>
+#include <sound/apr_audio.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/qdsp6v2/q6voice.h>
+#include <mach/qdsp6v2/audio_dev_ctl.h>
+#include "msm_audio_mvs.h"
+
+
+static struct audio_voip_info_type audio_voip_info;
+static void audio_mvs_process_ul_pkt(uint8_t *voc_pkt,
+ uint32_t pkt_len,
+ void *private_data);
+static void audio_mvs_process_dl_pkt(uint8_t *voc_pkt,
+ uint32_t *pkt_len,
+ void *private_data);
+
+struct msm_audio_mvs_frame {
+ uint32_t frame_type;
+ uint32_t len;
+ uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE];
+};
+
+struct audio_mvs_buf_node {
+ struct list_head list;
+ struct msm_audio_mvs_frame frame;
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_8000),
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN,
+ .period_bytes_min = MVS_MAX_VOC_PKT_SIZE,
+ .period_bytes_max = MVS_MAX_VOC_PKT_SIZE,
+ .periods_min = VOIP_MAX_Q_LEN,
+ .periods_max = VOIP_MAX_Q_LEN,
+ .fifo_size = 0,
+};
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+
+ struct audio_voip_info_type *audio = &audio_voip_info;
+ pr_debug("%s\n", __func__);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio->playback_start = 1;
+ else
+ audio->capture_start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio->playback_start = 0;
+ else
+ audio->capture_start = 0;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int rc = 0;
+ struct audio_voip_info_type *audio = &audio_voip_info;
+ struct audio_mvs_release_msg release_msg;
+
+ pr_debug("%s\n", __func__);
+ memset(&release_msg, 0, sizeof(release_msg));
+ mutex_lock(&audio->lock);
+
+ audio->instance--;
+ wake_up(&audio->out_wait);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio->playback_state = AUDIO_MVS_CLOSED;
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ audio->capture_state = AUDIO_MVS_CLOSED;
+ if (!audio->instance) {
+ /* Release MVS. */
+ release_msg.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
+ /* Derigstering the callbacks with voice driver */
+ voice_register_mvs_cb(NULL, NULL, audio);
+ } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ voice_register_mvs_cb(audio_mvs_process_ul_pkt,
+ NULL, audio);
+ } else {
+ voice_register_mvs_cb(NULL, audio_mvs_process_dl_pkt,
+ audio);
+ }
+
+ mutex_unlock(&audio->lock);
+
+ wake_unlock(&audio->suspend_lock);
+ wake_unlock(&audio->idle_lock);
+ /* Release the IO buffers. */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ audio->in_write = 0;
+ audio->in_read = 0;
+ memset(audio->in[0].voc_pkt, 0,
+ MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN);
+ audio->playback_substream = NULL;
+ } else {
+ audio->out_write = 0;
+ audio->out_read = 0;
+ memset(audio->out[0].voc_pkt, 0,
+ MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN);
+ audio->capture_substream = NULL;
+ }
+ return rc;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_voip_info_type *audio = &audio_voip_info;
+
+ pr_debug("%s\n", __func__);
+ mutex_lock(&audio->lock);
+
+ if (audio->playback_substream == NULL ||
+ audio->capture_substream == NULL) {
+ if (substream->stream ==
+ SNDRV_PCM_STREAM_PLAYBACK) {
+ audio->playback_substream = substream;
+ runtime->hw = msm_pcm_hardware;
+ audio_voip_info.in_read = 0;
+ audio_voip_info.in_write = 0;
+ if (audio->playback_state < AUDIO_MVS_OPENED)
+ audio->playback_state = AUDIO_MVS_OPENED;
+ } else if (substream->stream ==
+ SNDRV_PCM_STREAM_CAPTURE) {
+ audio->capture_substream = substream;
+ runtime->hw = msm_pcm_hardware;
+ audio_voip_info.out_read = 0;
+ audio_voip_info.out_write = 0;
+ if (audio->capture_state < AUDIO_MVS_OPENED)
+ audio->capture_state = AUDIO_MVS_OPENED;
+ }
+ } else {
+ ret = -EPERM;
+ goto err;
+ }
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ pr_debug("%s:snd_pcm_hw_constraint_integer failed\n", __func__);
+ goto err;
+ }
+ audio->instance++;
+
+err:
+ mutex_unlock(&audio->lock);
+ return ret;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int rc = 0;
+ int count = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_voip_info_type *audio = &audio_voip_info;
+ uint32_t index;
+ pr_debug("%s\n", __func__);
+
+ rc = wait_event_timeout(audio->in_wait,
+ (audio->in_write - audio->in_read <= VOIP_MAX_Q_LEN-1),
+ 1 * HZ);
+ if (rc < 0) {
+ pr_debug("%s: write was interrupted\n", __func__);
+ return -ERESTARTSYS;
+ }
+
+ if (audio->playback_state == AUDIO_MVS_ENABLED) {
+ index = audio->in_write % VOIP_MAX_Q_LEN;
+ count = frames_to_bytes(runtime, frames);
+ if (count == MVS_MAX_VOC_PKT_SIZE) {
+ pr_debug("%s:write index = %d\n", __func__, index);
+ rc = copy_from_user(audio->in[index].voc_pkt, buf,
+ count);
+ if (!rc) {
+ audio->in[index].len = count;
+ audio->in_write++;
+ } else {
+ pr_debug("%s:Copy from user returned %d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ }
+ } else
+ rc = -ENOMEM;
+
+ } else {
+ pr_debug("%s:Write performed in invalid state %d\n",
+ __func__, audio->playback_state);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff,
+ void __user *buf, snd_pcm_uframes_t frames)
+{
+ int rc = 0;
+ int count = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_voip_info_type *audio = &audio_voip_info;
+ uint32_t index = 0;
+
+ pr_debug("%s\n", __func__);
+
+ /* Ensure the driver has been enabled. */
+ if (audio->capture_state != AUDIO_MVS_ENABLED) {
+ pr_debug("%s:Read performed in invalid state %d\n",
+ __func__, audio->capture_state);
+ return -EPERM;
+ }
+ rc = wait_event_timeout(audio->out_wait,
+ ((audio->out_read < audio->out_write) ||
+ (audio->capture_state == AUDIO_MVS_CLOSING) ||
+ (audio->capture_state == AUDIO_MVS_CLOSED)),
+ 1 * HZ);
+
+ if (rc < 0) {
+ pr_debug("%s: Read was interrupted\n", __func__);
+ return -ERESTARTSYS;
+ }
+
+ if (audio->capture_state == AUDIO_MVS_CLOSING
+ || audio->capture_state == AUDIO_MVS_CLOSED) {
+ pr_debug("%s:EBUSY STATE\n", __func__);
+ rc = -EBUSY;
+ } else {
+ count = frames_to_bytes(runtime, frames);
+ index = audio->out_read % VOIP_MAX_Q_LEN;
+ pr_debug("%s:index=%d\n", __func__, index);
+ if (audio->out[index].len <= count) {
+ rc = copy_to_user(buf,
+ audio->out[index].voc_pkt,
+ audio->out[index].len);
+ if (rc) {
+ pr_debug("%s:Copy to user %d\n",
+ __func__, rc);
+ rc = -EFAULT;
+ } else
+ audio->out_read++;
+ } else {
+ pr_debug("%s:returning ENOMEM\n", __func__);
+ rc = -ENOMEM;
+ }
+ }
+ return rc;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ pr_debug("%s\n", __func__);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+/* Capture path */
+static void audio_mvs_process_ul_pkt(uint8_t *voc_pkt,
+ uint32_t pkt_len,
+ void *private_data)
+{
+ struct audio_voip_info_type *audio = private_data;
+ uint32_t index;
+ static int i;
+ pr_debug("%s\n", __func__);
+
+ if (audio->capture_substream == NULL)
+ return;
+ index = audio->out_write % VOIP_MAX_Q_LEN;
+ memcpy(audio->out[index].voc_pkt, voc_pkt, pkt_len);
+ audio->out[index].len = pkt_len;
+ audio->out_write++;
+ wake_up(&audio->out_wait);
+ i++;
+ if (audio->capture_start) {
+ audio->pcm_capture_irq_pos += audio->pcm_count;
+ if (!(i % 2))
+ snd_pcm_period_elapsed(audio->capture_substream);
+ }
+}
+
+/* Playback path */
+static void audio_mvs_process_dl_pkt(uint8_t *voc_pkt,
+ uint32_t *pkt_len,
+ void *private_data)
+{
+ struct audio_voip_info_type *audio = private_data;
+ uint32_t index;
+ static int i;
+ pr_debug("%s\n", __func__);
+
+ if (audio->playback_substream == NULL)
+ return;
+ if ((audio->in_write - audio->in_read >= 0)
+ && (audio->playback_start)) {
+ index = audio->in_read % VOIP_MAX_Q_LEN;
+ *pkt_len = audio->pcm_count;
+ memcpy(voc_pkt, audio->in[index].voc_pkt, *pkt_len);
+ audio->in_read++;
+ wake_up(&audio->in_wait);
+ i++;
+ audio->pcm_playback_irq_pos += audio->pcm_count;
+ if (!(i%2))
+ snd_pcm_period_elapsed(audio->playback_substream);
+ pr_debug("%s:read_index=%d\n", __func__, index);
+ }
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int rc = 0;
+ struct audio_voip_info_type *prtd = &audio_voip_info;
+ pr_debug("%s\n", __func__);
+ prtd->pcm_playback_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ pr_debug("%s:prtd->pcm_playback_size:%d\n",
+ __func__, prtd->pcm_playback_size);
+ pr_debug("%s:prtd->pcm_count:%d\n", __func__, prtd->pcm_count);
+
+ mutex_lock(&prtd->prepare_lock);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (prtd->playback_state == AUDIO_MVS_ENABLED)
+ goto enabled;
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (prtd->capture_state == AUDIO_MVS_ENABLED)
+ goto enabled;
+ }
+
+ pr_debug("%s:Register cbs with voice driver check audio_mvs_driver\n",
+ __func__);
+ if (prtd->instance == 2) {
+ voice_register_mvs_cb(audio_mvs_process_ul_pkt,
+ audio_mvs_process_dl_pkt,
+ prtd);
+ } else {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ voice_register_mvs_cb(NULL,
+ audio_mvs_process_dl_pkt,
+ prtd);
+ } else {
+ voice_register_mvs_cb(audio_mvs_process_ul_pkt,
+ NULL,
+ prtd);
+ }
+ }
+
+enabled:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->playback_state = AUDIO_MVS_ENABLED;
+ prtd->pcm_playback_irq_pos = 0;
+ prtd->pcm_playback_buf_pos = 0;
+ /* rate and channels are sent to audio driver */
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ prtd->capture_state = AUDIO_MVS_ENABLED;
+ prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_capture_irq_pos = 0;
+ prtd->pcm_capture_buf_pos = 0;
+ }
+ mutex_unlock(&prtd->prepare_lock);
+ return rc;
+}
+
+static snd_pcm_uframes_t
+msm_pcm_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_voip_info_type *audio = &audio_voip_info;
+
+ if (audio->pcm_playback_irq_pos >= audio->pcm_playback_size)
+ audio->pcm_playback_irq_pos = 0;
+ return bytes_to_frames(runtime, (audio->pcm_playback_irq_pos));
+}
+
+static snd_pcm_uframes_t
+msm_pcm_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_voip_info_type *audio = &audio_voip_info;
+
+ if (audio->pcm_capture_irq_pos >= audio->pcm_capture_size)
+ audio->pcm_capture_irq_pos = 0;
+ return bytes_to_frames(runtime, (audio->pcm_capture_irq_pos));
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t ret = 0;
+ pr_debug("%s\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_pointer(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_pointer(substream);
+ return ret;
+}
+
+static struct snd_pcm_ops msm_mvs_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+
+};
+
+static int msm_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ int i, ret, offset = 0;
+ struct snd_pcm_substream *substream = NULL;
+ struct snd_dma_buffer *dma_buffer = NULL;
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+
+ ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 1);
+ if (ret)
+ return ret;
+ ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
+ if (ret)
+ return ret;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &msm_mvs_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &msm_mvs_pcm_ops);
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (!substream)
+ return -ENOMEM;
+
+ dma_buffer = &substream->dma_buffer;
+ dma_buffer->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buffer->dev.dev = card->dev;
+ dma_buffer->private_data = NULL;
+ dma_buffer->area = dma_alloc_coherent(card->dev,
+ (MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN),
+ &dma_buffer->addr, GFP_KERNEL);
+ if (!dma_buffer->area) {
+ pr_err("%s:MSM VOIP dma_alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ dma_buffer->bytes = MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN;
+ memset(dma_buffer->area, 0, MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN);
+ audio_voip_info.in_read = 0;
+ audio_voip_info.in_write = 0;
+ audio_voip_info.out_read = 0;
+ audio_voip_info.out_write = 0;
+ for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
+ audio_voip_info.in[i].voc_pkt =
+ dma_buffer->area + offset;
+ offset = offset + MVS_MAX_VOC_PKT_SIZE;
+ }
+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (!substream)
+ return -ENOMEM;
+
+ dma_buffer = &substream->dma_buffer;
+ dma_buffer->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buffer->dev.dev = card->dev;
+ dma_buffer->private_data = NULL;
+ dma_buffer->area = dma_alloc_coherent(card->dev,
+ (MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN),
+ &dma_buffer->addr, GFP_KERNEL);
+ if (!dma_buffer->area) {
+ pr_err("%s:MSM VOIP dma_alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ memset(dma_buffer->area, 0, MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN);
+ dma_buffer->bytes = MVS_MAX_VOC_PKT_SIZE * VOIP_MAX_Q_LEN;
+ for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
+ audio_voip_info.out[i].voc_pkt =
+ dma_buffer->area + offset;
+ offset = offset + MVS_MAX_VOC_PKT_SIZE;
+ }
+ audio_voip_info.playback_substream = NULL;
+ audio_voip_info.capture_substream = NULL;
+
+ return 0;
+}
+
+static void msm_pcm_free_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_coherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+struct snd_soc_platform_driver msm_mvs_soc_platform = {
+ .ops = &msm_mvs_pcm_ops,
+ .pcm_new = msm_pcm_new,
+ .pcm_free = msm_pcm_free_buffers,
+};
+EXPORT_SYMBOL(msm_mvs_soc_platform);
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_mvs_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-mvs-audio",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_mvs_soc_platform_init(void)
+{
+ memset(&audio_voip_info, 0, sizeof(audio_voip_info));
+ mutex_init(&audio_voip_info.lock);
+ mutex_init(&audio_voip_info.prepare_lock);
+ init_waitqueue_head(&audio_voip_info.out_wait);
+ init_waitqueue_head(&audio_voip_info.in_wait);
+ wake_lock_init(&audio_voip_info.suspend_lock, WAKE_LOCK_SUSPEND,
+ "audio_mvs_suspend");
+ wake_lock_init(&audio_voip_info.idle_lock, WAKE_LOCK_IDLE,
+ "audio_mvs_idle");
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_mvs_soc_platform_init);
+
+static void __exit msm_mvs_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_mvs_soc_platform_exit);
+
+MODULE_DESCRIPTION("MVS PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm7201.c b/sound/soc/msm/msm7201.c
new file mode 100644
index 0000000..061529a
--- /dev/null
+++ b/sound/soc/msm/msm7201.c
@@ -0,0 +1,329 @@
+/* linux/sound/soc/msm/msm7201.c
+ *
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include "msm-pcm.h"
+#include <asm/mach-types.h>
+#include <mach/msm_rpcrouter.h>
+
+static struct msm_rpc_endpoint *snd_ep;
+
+struct msm_snd_rpc_ids {
+ unsigned long prog;
+ unsigned long vers;
+ unsigned long rpc_set_snd_device;
+ int device;
+};
+
+static struct msm_snd_rpc_ids snd_rpc_ids;
+
+static struct platform_device *msm_audio_snd_device;
+
+static int snd_msm_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1; /* Volume Param, in dB */
+ uinfo->value.integer.min = MIN_DB;
+ uinfo->value.integer.max = MAX_DB;
+ return 0;
+}
+
+static int snd_msm_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ spin_lock_irq(&the_locks.mixer_lock);
+ ucontrol->value.integer.value[0] = msm_vol_ctl.volume;
+ spin_unlock_irq(&the_locks.mixer_lock);
+ return 0;
+}
+
+static int snd_msm_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int change;
+ int volume;
+
+ volume = ucontrol->value.integer.value[0];
+ spin_lock_irq(&the_locks.mixer_lock);
+ change = (msm_vol_ctl.volume != volume);
+ if (change) {
+ msm_vol_ctl.update = 1;
+ msm_vol_ctl.volume = volume;
+ }
+ spin_unlock_irq(&the_locks.mixer_lock);
+ return change;
+}
+
+static int snd_msm_device_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1; /* Device */
+
+ /*
+ * The number of devices supported is 26 (0 to 25)
+ */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 25;
+ return 0;
+}
+
+static int snd_msm_device_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = (uint32_t)snd_rpc_ids.device;
+ return 0;
+}
+
+int msm_snd_init_rpc_ids(void)
+{
+ snd_rpc_ids.prog = 0x30000002;
+ snd_rpc_ids.vers = 0x00020001;
+ /*
+ * The magic number 2 corresponds to the rpc call
+ * index for snd_set_device
+ */
+ snd_rpc_ids.rpc_set_snd_device = 2;
+ return 0;
+}
+
+int msm_snd_rpc_connect(void)
+{
+ if (snd_ep) {
+ printk(KERN_INFO "%s: snd_ep already connected\n", __func__);
+ return 0;
+ }
+
+ /* Initialize rpc ids */
+ if (msm_snd_init_rpc_ids()) {
+ printk(KERN_ERR "%s: snd rpc ids initialization failed\n"
+ , __func__);
+ return -ENODATA;
+ }
+
+ snd_ep = msm_rpc_connect_compatible(snd_rpc_ids.prog,
+ snd_rpc_ids.vers, 0);
+ if (IS_ERR(snd_ep)) {
+ printk(KERN_ERR "%s: failed (compatible VERS = %ld)\n",
+ __func__, snd_rpc_ids.vers);
+ snd_ep = NULL;
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+int msm_snd_rpc_close(void)
+{
+ int rc = 0;
+
+ if (IS_ERR(snd_ep)) {
+ printk(KERN_ERR "%s: snd handle unavailable, rc = %ld\n",
+ __func__, PTR_ERR(snd_ep));
+ return -EAGAIN;
+ }
+
+ rc = msm_rpc_close(snd_ep);
+ snd_ep = NULL;
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: close rpc failed! rc = %d\n",
+ __func__, rc);
+ return -EAGAIN;
+ } else
+ printk(KERN_INFO "rpc close success\n");
+
+ return rc;
+}
+
+static int snd_msm_device_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct snd_start_req {
+ struct rpc_request_hdr hdr;
+ uint32_t rpc_snd_device;
+ uint32_t snd_mute_ear_mute;
+ uint32_t snd_mute_mic_mute;
+ uint32_t callback_ptr;
+ uint32_t client_data;
+ } req;
+
+ snd_rpc_ids.device = (int)ucontrol->value.integer.value[0];
+ req.hdr.type = 0;
+ req.hdr.rpc_vers = 2;
+
+ req.rpc_snd_device = cpu_to_be32(snd_rpc_ids.device);
+ req.snd_mute_ear_mute = cpu_to_be32(1);
+ req.snd_mute_mic_mute = cpu_to_be32(0);
+ req.callback_ptr = -1;
+ req.client_data = cpu_to_be32(0);
+
+ req.hdr.prog = snd_rpc_ids.prog;
+ req.hdr.vers = snd_rpc_ids.vers;
+
+ rc = msm_rpc_call(snd_ep, snd_rpc_ids.rpc_set_snd_device ,
+ &req, sizeof(req), 5 * HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: snd rpc call failed! rc = %d\n",
+ __func__, rc);
+ } else
+ printk(KERN_INFO "snd device connected \n");
+
+ return rc;
+}
+
+/* Supported range -50dB to 18dB */
+static const DECLARE_TLV_DB_LINEAR(db_scale_linear, -5000, 1800);
+
+#define MSM_EXT(xname, xindex, fp_info, fp_get, fp_put, addr) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .name = xname, .index = xindex, \
+ .info = fp_info,\
+ .get = fp_get, .put = fp_put, \
+ .private_value = addr, \
+}
+
+#define MSM_EXT_TLV(xname, xindex, fp_info, fp_get, fp_put, addr, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = (SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE), \
+ .name = xname, .index = xindex, \
+ .info = fp_info,\
+ .get = fp_get, .put = fp_put, .tlv.p = tlv_array, \
+ .private_value = addr, \
+}
+
+static struct snd_kcontrol_new snd_msm_controls[] = {
+ MSM_EXT_TLV("PCM Playback Volume", 0, snd_msm_volume_info, \
+ snd_msm_volume_get, snd_msm_volume_put, 0, db_scale_linear),
+ MSM_EXT("device", 1, snd_msm_device_info, snd_msm_device_get, \
+ snd_msm_device_put, 0),
+};
+
+static int msm_new_mixer(struct snd_soc_codec *codec)
+{
+ unsigned int idx;
+ int err;
+
+ pr_err("msm_soc: ALSA MSM Mixer Setting\n");
+ strcpy(codec->card->snd_card->mixername, "MSM Mixer");
+ for (idx = 0; idx < ARRAY_SIZE(snd_msm_controls); idx++) {
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&snd_msm_controls[idx], NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int msm_soc_dai_init(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ mutex_init(&the_locks.lock);
+ mutex_init(&the_locks.write_lock);
+ mutex_init(&the_locks.read_lock);
+ spin_lock_init(&the_locks.read_dsp_lock);
+ spin_lock_init(&the_locks.write_dsp_lock);
+ spin_lock_init(&the_locks.mixer_lock);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+ msm_vol_ctl.volume = MSM_PLAYBACK_DEFAULT_VOLUME;
+ msm_vol_ctl.pan = MSM_PLAYBACK_DEFAULT_PAN;
+
+ ret = msm_new_mixer(codec);
+ if (ret < 0) {
+ pr_err("msm_soc: ALSA MSM Mixer Fail\n");
+ }
+
+ return ret;
+}
+
+static struct snd_soc_dai_link msm_dai[] = {
+{
+ .name = "MSM Primary I2S",
+ .stream_name = "DSP 1",
+ .cpu_dai_name = "msm-cpu-dai.0",
+ .platform_name = "msm-dsp-audio.0",
+ .codec_name = "msm-codec-dai.0",
+ .codec_dai_name = "msm-codec-dai",
+ .init = &msm_soc_dai_init,
+},
+};
+
+static struct snd_soc_card snd_soc_card_msm = {
+ .name = "msm-audio",
+ .dai_link = msm_dai,
+ .num_links = ARRAY_SIZE(msm_dai),
+};
+
+static int __init msm_audio_init(void)
+{
+ int ret;
+
+ msm_audio_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!msm_audio_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(msm_audio_snd_device, &snd_soc_card_msm);
+ ret = platform_device_add(msm_audio_snd_device);
+ if (ret) {
+ platform_device_put(msm_audio_snd_device);
+ return ret;
+ }
+
+ ret = msm_snd_rpc_connect();
+
+ return ret;
+}
+
+static void __exit msm_audio_exit(void)
+{
+ msm_snd_rpc_close();
+ platform_device_unregister(msm_audio_snd_device);
+}
+
+module_init(msm_audio_init);
+module_exit(msm_audio_exit);
+
+MODULE_DESCRIPTION("PCM module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm7k-pcm.c b/sound/soc/msm/msm7k-pcm.c
new file mode 100644
index 0000000..1f36e3b
--- /dev/null
+++ b/sound/soc/msm/msm7k-pcm.c
@@ -0,0 +1,600 @@
+/* linux/sound/soc/msm/msm7k-pcm.c
+ *
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include "msm-pcm.h"
+
+#define SND_DRIVER "snd_msm"
+#define MAX_PCM_DEVICES SNDRV_CARDS
+#define MAX_PCM_SUBSTREAMS 1
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+int copy_count;
+
+struct audio_locks the_locks;
+EXPORT_SYMBOL(the_locks);
+struct msm_volume msm_vol_ctl;
+EXPORT_SYMBOL(msm_vol_ctl);
+
+
+static unsigned convert_dsp_samp_index(unsigned index)
+{
+ switch (index) {
+ case 48000:
+ return AUDREC_CMD_SAMP_RATE_INDX_48000;
+ case 44100:
+ return AUDREC_CMD_SAMP_RATE_INDX_44100;
+ case 32000:
+ return AUDREC_CMD_SAMP_RATE_INDX_32000;
+ case 24000:
+ return AUDREC_CMD_SAMP_RATE_INDX_24000;
+ case 22050:
+ return AUDREC_CMD_SAMP_RATE_INDX_22050;
+ case 16000:
+ return AUDREC_CMD_SAMP_RATE_INDX_16000;
+ case 12000:
+ return AUDREC_CMD_SAMP_RATE_INDX_12000;
+ case 11025:
+ return AUDREC_CMD_SAMP_RATE_INDX_11025;
+ case 8000:
+ return AUDREC_CMD_SAMP_RATE_INDX_8000;
+ default:
+ return AUDREC_CMD_SAMP_RATE_INDX_44100;
+ }
+}
+
+static unsigned convert_samp_rate(unsigned hz)
+{
+ switch (hz) {
+ case 48000:
+ return RPC_AUD_DEF_SAMPLE_RATE_48000;
+ case 44100:
+ return RPC_AUD_DEF_SAMPLE_RATE_44100;
+ case 32000:
+ return RPC_AUD_DEF_SAMPLE_RATE_32000;
+ case 24000:
+ return RPC_AUD_DEF_SAMPLE_RATE_24000;
+ case 22050:
+ return RPC_AUD_DEF_SAMPLE_RATE_22050;
+ case 16000:
+ return RPC_AUD_DEF_SAMPLE_RATE_16000;
+ case 12000:
+ return RPC_AUD_DEF_SAMPLE_RATE_12000;
+ case 11025:
+ return RPC_AUD_DEF_SAMPLE_RATE_11025;
+ case 8000:
+ return RPC_AUD_DEF_SAMPLE_RATE_8000;
+ default:
+ return RPC_AUD_DEF_SAMPLE_RATE_44100;
+ }
+}
+
+static struct snd_pcm_hardware msm_pcm_playback_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = USE_FORMATS,
+ .rates = USE_RATE,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .channels_min = USE_CHANNELS_MIN,
+ .channels_max = USE_CHANNELS_MAX,
+ .buffer_bytes_max = MAX_BUFFER_PLAYBACK_SIZE,
+ .period_bytes_min = 64,
+ .period_bytes_max = MAX_PERIOD_SIZE,
+ .periods_min = USE_PERIODS_MIN,
+ .periods_max = USE_PERIODS_MAX,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware msm_pcm_capture_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = USE_FORMATS,
+ .rates = USE_RATE,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .channels_min = USE_CHANNELS_MIN,
+ .channels_max = USE_CHANNELS_MAX,
+ .buffer_bytes_max = MAX_BUFFER_CAPTURE_SIZE,
+ .period_bytes_min = CAPTURE_SIZE,
+ .period_bytes_max = CAPTURE_SIZE,
+ .periods_min = USE_PERIODS_MIN,
+ .periods_max = USE_PERIODS_MAX,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void playback_event_handler(void *data)
+{
+ struct msm_audio *prtd = data;
+ snd_pcm_period_elapsed(prtd->playback_substream);
+}
+
+static void capture_event_handler(void *data)
+{
+ struct msm_audio *prtd = data;
+ snd_pcm_period_elapsed(prtd->capture_substream);
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ prtd->pcm_buf_pos = 0;
+
+ /* rate and channels are sent to audio driver */
+ prtd->out_sample_rate = runtime->rate;
+ prtd->out_channel_mode = runtime->channels;
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct audmgr_config cfg;
+ int rc;
+
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ prtd->pcm_buf_pos = 0;
+
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = convert_samp_rate(runtime->rate);
+ prtd->samp_rate_index = convert_dsp_samp_index(runtime->rate);
+ prtd->channel_mode = (runtime->channels - 1);
+ prtd->buffer_size = prtd->channel_mode ? STEREO_DATA_SIZE : \
+ MONO_DATA_SIZE;
+
+ if (prtd->enabled == 1)
+ return 0;
+
+ prtd->type = AUDREC_CMD_TYPE_0_INDEX_WAV;
+
+ cfg.tx_rate = convert_samp_rate(runtime->rate);
+ cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+ cfg.def_method = RPC_AUD_DEF_METHOD_RECORD;
+ cfg.codec = RPC_AUD_DEF_CODEC_PCM;
+ cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+ rc = audmgr_enable(&prtd->audmgr, &cfg);
+ if (rc < 0)
+ return rc;
+
+ if (msm_adsp_enable(prtd->audpre)) {
+ audmgr_disable(&prtd->audmgr);
+ return -ENODEV;
+ }
+ if (msm_adsp_enable(prtd->audrec)) {
+ msm_adsp_disable(prtd->audpre);
+ audmgr_disable(&prtd->audmgr);
+ return -ENODEV;
+ }
+ prtd->enabled = 1;
+ alsa_rec_dsp_enable(prtd, 1);
+
+ return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t
+msm_pcm_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos == prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int rc = 0, rc1 = 0, rc2 = 0;
+ int fbytes = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = substream->runtime->private_data;
+
+ int monofbytes = 0;
+ char *bufferp = NULL;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ monofbytes = fbytes / 2;
+ if (runtime->channels == 2) {
+ rc = alsa_buffer_read(prtd, buf, fbytes, NULL);
+ } else {
+ bufferp = buf;
+ rc1 = alsa_buffer_read(prtd, bufferp, monofbytes, NULL);
+ bufferp = buf + monofbytes ;
+ rc2 = alsa_buffer_read(prtd, bufferp, monofbytes, NULL);
+ rc = rc1 + rc2;
+ }
+ prtd->pcm_buf_pos += fbytes;
+ return rc;
+}
+
+static snd_pcm_uframes_t
+msm_pcm_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ alsa_audrec_disable(prtd);
+ audmgr_close(&prtd->audmgr);
+ msm_adsp_put(prtd->audrec);
+ msm_adsp_put(prtd->audpre);
+ kfree(prtd);
+
+ return 0;
+}
+
+struct msm_audio_event_callbacks snd_msm_audio_ops = {
+ .playback = playback_event_handler,
+ .capture = capture_event_handler,
+};
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd;
+ int ret = 0;
+
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msm_vol_ctl.update = 1; /* Update Volume, with Cached value */
+ runtime->hw = msm_pcm_playback_hardware;
+ prtd->dir = SNDRV_PCM_STREAM_PLAYBACK;
+ prtd->playback_substream = substream;
+ prtd->eos_ack = 0;
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw = msm_pcm_capture_hardware;
+ prtd->dir = SNDRV_PCM_STREAM_CAPTURE;
+ prtd->capture_substream = substream;
+ }
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ goto out;
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ prtd->ops = &snd_msm_audio_ops;
+ prtd->out[0].used = BUF_INVALID_LEN;
+ prtd->out_head = 1; /* point to second buffer on startup */
+ runtime->private_data = prtd;
+
+ ret = alsa_adsp_configure(prtd);
+ if (ret)
+ goto out;
+ copy_count = 0;
+ return 0;
+
+ out:
+ kfree(prtd);
+ return ret;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int rc = 1;
+ int fbytes = 0;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ rc = alsa_send_buffer(prtd, buf, fbytes, NULL);
+ ++copy_count;
+ prtd->pcm_buf_pos += fbytes;
+ if (copy_count == 1) {
+ mutex_lock(&the_locks.lock);
+ alsa_audio_configure(prtd);
+ mutex_unlock(&the_locks.lock);
+ }
+ if ((prtd->running) && (msm_vol_ctl.update)) {
+ rc = msm_audio_volume_update(PCMPLAYBACK_DECODERID,
+ msm_vol_ctl.volume, msm_vol_ctl.pan);
+ msm_vol_ctl.update = 0;
+ }
+
+ return rc;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ int rc = 0;
+
+ pr_debug("%s()\n", __func__);
+
+ /* pcm dmamiss message is sent continously
+ * when decoder is starved so no race
+ * condition concern
+ */
+ if (prtd->enabled)
+ rc = wait_event_interruptible(the_locks.eos_wait,
+ prtd->eos_ack);
+
+ alsa_audio_disable(prtd);
+ audmgr_close(&prtd->audmgr);
+ kfree(prtd);
+
+ return 0;
+}
+
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_pointer(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_pointer(substream);
+ return ret;
+}
+
+int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (substream->pcm->device & 1) {
+ runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
+ runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
+ }
+ return 0;
+
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+};
+
+static int pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
+ int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size;
+ if (!stream)
+ size = PLAYBACK_DMASZ;
+ else
+ size = CAPTURE_DMASZ;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_coherent(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+ return 0;
+}
+
+static void msm_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_coherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+static int msm_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 1);
+ if (ret)
+ return ret;
+ ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
+ if (ret)
+ return ret;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &msm_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &msm_pcm_ops);
+
+ ret = pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ return ret;
+
+ ret = pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ msm_pcm_free_dma_buffers(pcm);
+ return ret;
+}
+
+struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_pcm_new,
+ .pcm_free = msm_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL(msm_soc_platform);
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-dsp-audio",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm7kv2-dai.c b/sound/soc/msm/msm7kv2-dai.c
new file mode 100644
index 0000000..e8d51ac
--- /dev/null
+++ b/sound/soc/msm/msm7kv2-dai.c
@@ -0,0 +1,149 @@
+/* sound/soc/msm/msm-dai.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * Derived from msm-pcm.c and msm7201.c.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <linux/slab.h>
+#include "msm7kv2-pcm.h"
+
+static struct snd_soc_dai_driver msm_pcm_codec_dais[] = {
+{
+ .name = "msm-codec-dai",
+ .playback = {
+ .channels_max = USE_CHANNELS_MAX,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_max = USE_CHANNELS_MAX,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+static struct snd_soc_dai_driver msm_pcm_cpu_dais[] = {
+{
+ .name = "msm-cpu-dai",
+ .playback = {
+ .channels_max = USE_CHANNELS_MAX,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_max = USE_CHANNELS_MAX,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_msm = {
+ .compress_type = SND_SOC_FLAT_COMPRESSION,
+};
+
+static __devinit int asoc_msm_codec_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm,
+ msm_pcm_codec_dais, ARRAY_SIZE(msm_pcm_codec_dais));
+}
+
+static int __devexit asoc_msm_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static __devinit int asoc_msm_cpu_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_dai(&pdev->dev, msm_pcm_cpu_dais);
+}
+
+static int __devexit asoc_msm_cpu_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver asoc_msm_codec_driver = {
+ .probe = asoc_msm_codec_probe,
+ .remove = __devexit_p(asoc_msm_codec_remove),
+ .driver = {
+ .name = "msm-codec-dai",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_driver asoc_msm_cpu_driver = {
+ .probe = asoc_msm_cpu_probe,
+ .remove = __devexit_p(asoc_msm_cpu_remove),
+ .driver = {
+ .name = "msm-cpu-dai",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_codec_dai_init(void)
+{
+ return platform_driver_register(&asoc_msm_codec_driver);
+}
+
+static void __exit msm_codec_dai_exit(void)
+{
+ platform_driver_unregister(&asoc_msm_codec_driver);
+}
+
+static int __init msm_cpu_dai_init(void)
+{
+ return platform_driver_register(&asoc_msm_cpu_driver);
+}
+
+static void __exit msm_cpu_dai_exit(void)
+{
+ platform_driver_unregister(&asoc_msm_cpu_driver);
+}
+
+module_init(msm_codec_dai_init);
+module_exit(msm_codec_dai_exit);
+module_init(msm_cpu_dai_init);
+module_exit(msm_cpu_dai_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM Codec/Cpu Dai driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm7kv2-dsp.c b/sound/soc/msm/msm7kv2-dsp.c
new file mode 100644
index 0000000..50bf6fb
--- /dev/null
+++ b/sound/soc/msm/msm7kv2-dsp.c
@@ -0,0 +1,633 @@
+/* Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+#include "msm7kv2-pcm.h"
+
+/* Audrec Queue command sent macro's */
+#define audrec_send_bitstreamqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, ((audio->queue_id & 0xFFFF0000) >> 16),\
+ cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+ msm_adsp_write(audio->audrec, (audio->queue_id & 0x0000FFFF),\
+ cmd, len)
+
+static int alsa_dsp_read_buffer(struct msm_audio *audio,
+ uint32_t read_cnt);
+static void alsa_get_dsp_frames(struct msm_audio *prtd);
+static int alsa_in_param_config(struct msm_audio *audio);
+
+static int alsa_in_mem_config(struct msm_audio *audio);
+static int alsa_in_enc_config(struct msm_audio *audio, int enable);
+
+int intcnt;
+struct audio_frame {
+ uint16_t count_low;
+ uint16_t count_high;
+ uint16_t bytes;
+ uint16_t unknown;
+ unsigned char samples[];
+} __attribute__ ((packed));
+
+void alsa_dsp_event(void *data, unsigned id, uint16_t *msg)
+{
+ struct msm_audio *prtd = data;
+ struct buffer *frame;
+ unsigned long flag = 0;
+
+ MM_DBG("\n");
+ switch (id) {
+ case AUDPP_MSG_HOST_PCM_INTF_MSG: {
+ unsigned id = msg[3];
+ unsigned idx = msg[4] - 1;
+
+ MM_DBG("HOST_PCM id %d idx %d\n", id, idx);
+ if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
+ MM_ERR("bogus id\n");
+ break;
+ }
+ if (idx > 1) {
+ MM_ERR("bogus buffer idx\n");
+ break;
+ }
+
+ /* Update with actual sent buffer size */
+ if (prtd->out[idx].used != BUF_INVALID_LEN)
+ prtd->pcm_irq_pos += prtd->out[idx].used;
+
+ if (prtd->pcm_irq_pos > prtd->pcm_size)
+ prtd->pcm_irq_pos = prtd->pcm_count;
+
+ if (prtd->ops->playback)
+ prtd->ops->playback(prtd);
+
+ if (prtd->mmap_flag)
+ break;
+
+ spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
+ if (prtd->running) {
+ prtd->out[idx].used = 0;
+ frame = prtd->out + prtd->out_tail;
+ if (frame->used) {
+ alsa_dsp_send_buffer(
+ prtd, prtd->out_tail, frame->used);
+ /* Reset eos_ack flag to avoid stale
+ * PCMDMAMISS been considered
+ */
+ prtd->eos_ack = 0;
+ prtd->out_tail ^= 1;
+ } else {
+ prtd->out_needed++;
+ }
+ wake_up(&the_locks.write_wait);
+ }
+ spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
+ break;
+ }
+ case AUDPP_MSG_PCMDMAMISSED:
+ MM_INFO("PCMDMAMISSED %d\n", msg[0]);
+ prtd->eos_ack++;
+ MM_DBG("PCMDMAMISSED Count per Buffer %d\n", prtd->eos_ack);
+ wake_up(&the_locks.eos_wait);
+ break;
+ case AUDPP_MSG_CFG_MSG:
+ if (msg[0] == AUDPP_MSG_ENA_ENA) {
+ MM_DBG("CFG_MSG ENABLE\n");
+ prtd->out_needed = 0;
+ prtd->running = 1;
+ audpp_dsp_set_vol_pan(prtd->session_id, &prtd->vol_pan,
+ POPP);
+ audpp_route_stream(prtd->session_id,
+ msm_snddev_route_dec(prtd->session_id));
+ audio_dsp_out_enable(prtd, 1);
+ } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+ MM_DBG("CFG_MSG DISABLE\n");
+ prtd->running = 0;
+ } else {
+ MM_DBG("CFG_MSG %d?\n", msg[0]);
+ }
+ break;
+ default:
+ MM_DBG("UNKNOWN (%d)\n", id);
+ }
+}
+
+static void audpreproc_dsp_event(void *data, unsigned id, void *msg)
+{
+ struct msm_audio *prtd = data;
+
+ switch (id) {
+ case AUDPREPROC_ERROR_MSG: {
+ struct audpreproc_err_msg *err_msg = msg;
+
+ MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+ err_msg->stream_id, err_msg->aud_preproc_err_idx);
+ /* Error case */
+ break;
+ }
+ case AUDPREPROC_CMD_CFG_DONE_MSG: {
+ MM_DBG("CMD_CFG_DONE_MSG\n");
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+ struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+ MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+ 0x%8x\n", enc_cfg_msg->stream_id,
+ enc_cfg_msg->rec_enc_type);
+ /* Encoder enable success */
+ if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE)
+ alsa_in_param_config(prtd);
+ else { /* Encoder disable success */
+ prtd->running = 0;
+ alsa_in_record_config(prtd, 0);
+ }
+ break;
+ }
+ case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+ MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n");
+ alsa_in_mem_config(prtd);
+ break;
+ }
+ case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+ MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG\n");
+ wake_up(&the_locks.enable_wait);
+ break;
+ }
+ default:
+ MM_DBG("Unknown Event id %d\n", id);
+ }
+}
+
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+ void (*getevent) (void *ptr, size_t len))
+{
+ struct msm_audio *prtd = data;
+ unsigned long flag = 0;
+
+ switch (id) {
+ case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+ MM_DBG("AUDREC_CMD_MEM_CFG_DONE_MSG\n");
+ prtd->running = 1;
+ alsa_in_record_config(prtd, 1);
+ break;
+ }
+ case AUDREC_FATAL_ERR_MSG: {
+ struct audrec_fatal_err_msg fatal_err_msg;
+
+ getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+ MM_ERR("FATAL_ERR_MSG: err id %d\n",
+ fatal_err_msg.audrec_err_id);
+ /* Error stop the encoder */
+ prtd->stopped = 1;
+ wake_up(&the_locks.read_wait);
+ break;
+ }
+ case AUDREC_UP_PACKET_READY_MSG: {
+ struct audrec_up_pkt_ready_msg pkt_ready_msg;
+ MM_DBG("AUDREC_UP_PACKET_READY_MSG\n");
+
+ getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+ MM_DBG("UP_PACKET_READY_MSG: write cnt lsw %d \
+ write cnt msw %d read cnt lsw %d read cnt msw %d \n",\
+ pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+ pkt_ready_msg.audrec_packet_write_cnt_msw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+ pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+ alsa_get_dsp_frames(prtd);
+ ++intcnt;
+ if (prtd->channel_mode == 1) {
+ spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+ spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
+
+ if (prtd->ops->capture)
+ prtd->ops->capture(prtd);
+ } else if ((prtd->channel_mode == 0) && (intcnt % 2 == 0)) {
+ spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+ spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
+ if (prtd->ops->capture)
+ prtd->ops->capture(prtd);
+ }
+ break;
+ }
+ default:
+ MM_DBG("Unknown Event id %d\n", id);
+ }
+}
+
+struct msm_adsp_ops alsa_audrec_adsp_ops = {
+ .event = audrec_dsp_event,
+};
+
+int alsa_audio_configure(struct msm_audio *prtd)
+{
+ if (prtd->enabled)
+ return 0;
+
+ MM_DBG("\n");
+ if (prtd->dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->out_weight = 100;
+ if (audpp_enable(-1, alsa_dsp_event, prtd)) {
+ MM_ERR("audpp_enable() failed\n");
+ return -ENODEV;
+ }
+ }
+ if (prtd->dir == SNDRV_PCM_STREAM_CAPTURE) {
+ if (audpreproc_enable(prtd->session_id,
+ &audpreproc_dsp_event, prtd)) {
+ MM_ERR("audpreproc_enable failed\n");
+ return -ENODEV;
+ }
+
+ if (msm_adsp_enable(prtd->audrec)) {
+ MM_ERR("msm_adsp_enable(audrec) enable failed\n");
+ audpreproc_disable(prtd->session_id, prtd);
+ return -ENODEV;
+ }
+ alsa_in_enc_config(prtd, 1);
+
+ }
+ prtd->enabled = 1;
+ return 0;
+}
+EXPORT_SYMBOL(alsa_audio_configure);
+
+ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ unsigned long flag = 0;
+ const char __user *start = buf;
+ struct buffer *frame;
+ size_t xfer;
+ int ret = 0;
+
+ MM_DBG("\n");
+ mutex_lock(&the_locks.write_lock);
+ while (count > 0) {
+ frame = prtd->out + prtd->out_head;
+ ret = wait_event_interruptible(the_locks.write_wait,
+ (frame->used == 0)
+ || (prtd->stopped));
+ if (ret < 0)
+ break;
+ if (prtd->stopped) {
+ ret = -EBUSY;
+ break;
+ }
+ xfer = count > frame->size ? frame->size : count;
+ if (copy_from_user(frame->data, buf, xfer)) {
+ ret = -EFAULT;
+ break;
+ }
+ frame->used = xfer;
+ prtd->out_head ^= 1;
+ count -= xfer;
+ buf += xfer;
+
+ spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
+ frame = prtd->out + prtd->out_tail;
+ if (frame->used && prtd->out_needed) {
+ alsa_dsp_send_buffer(prtd, prtd->out_tail,
+ frame->used);
+ /* Reset eos_ack flag to avoid stale
+ * PCMDMAMISS been considered
+ */
+ prtd->eos_ack = 0;
+ prtd->out_tail ^= 1;
+ prtd->out_needed--;
+ }
+ spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
+ }
+ mutex_unlock(&the_locks.write_lock);
+ if (buf > start)
+ return buf - start;
+ return ret;
+}
+EXPORT_SYMBOL(alsa_send_buffer);
+
+int alsa_audio_disable(struct msm_audio *prtd)
+{
+ if (prtd->enabled) {
+ MM_DBG("\n");
+ mutex_lock(&the_locks.lock);
+ prtd->enabled = 0;
+ audio_dsp_out_enable(prtd, 0);
+ wake_up(&the_locks.write_wait);
+ audpp_disable(-1, prtd);
+ prtd->out_needed = 0;
+ mutex_unlock(&the_locks.lock);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(alsa_audio_disable);
+
+int alsa_audrec_disable(struct msm_audio *prtd)
+{
+ if (prtd->enabled) {
+ prtd->enabled = 0;
+ alsa_in_enc_config(prtd, 0);
+ wake_up(&the_locks.read_wait);
+ msm_adsp_disable(prtd->audrec);
+ prtd->out_needed = 0;
+ audpreproc_disable(prtd->session_id, prtd);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(alsa_audrec_disable);
+
+static int alsa_in_enc_config(struct msm_audio *prtd, int enable)
+{
+ struct audpreproc_audrec_cmd_enc_cfg cmd;
+ int i;
+ unsigned short *ptrmem = (unsigned short *)&cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG;
+ cmd.stream_id = prtd->session_id;
+
+ if (enable)
+ cmd.audrec_enc_type = prtd->type | ENCODE_ENABLE;
+ else
+ cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+ for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+ MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int alsa_in_param_config(struct msm_audio *prtd)
+{
+ struct audpreproc_audrec_cmd_parm_cfg_wav cmd;
+ int i;
+ unsigned short *ptrmem = (unsigned short *)&cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+ cmd.common.stream_id = prtd->session_id;
+
+ cmd.aud_rec_samplerate_idx = prtd->samp_rate;
+ cmd.aud_rec_stereo_mode = prtd->channel_mode;
+ for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+ MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+int alsa_in_record_config(struct msm_audio *prtd, int enable)
+{
+ struct audpreproc_afe_cmd_audio_record_cfg cmd;
+ int i;
+ unsigned short *ptrmem = (unsigned short *)&cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+ cmd.stream_id = prtd->session_id;
+ if (enable)
+ cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+ else
+ cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+ cmd.source_mix_mask = prtd->source;
+ if (prtd->session_id == 2) {
+ if ((cmd.source_mix_mask &
+ INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+ (cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+ cmd.pipe_id = SOURCE_PIPE_1;
+ }
+ if (cmd.source_mix_mask &
+ AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+ cmd.pipe_id |= SOURCE_PIPE_0;
+ }
+ for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+ MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+ return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int alsa_in_mem_config(struct msm_audio *prtd)
+{
+ struct audrec_cmd_arecmem_cfg cmd;
+ uint16_t *data = (void *) prtd->data;
+ int n;
+ int i;
+ unsigned short *ptrmem = (unsigned short *)&cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+ cmd.audrec_up_pkt_intm_count = 1;
+ cmd.audrec_ext_pkt_start_addr_msw = prtd->phys >> 16;
+ cmd.audrec_ext_pkt_start_addr_lsw = prtd->phys;
+ cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+
+ /* prepare buffer pointers:
+ * Mono: 1024 samples + 4 halfword header
+ * Stereo: 2048 samples + 4 halfword header
+ */
+ for (n = 0; n < FRAME_NUM; n++) {
+ prtd->in[n].data = data + 4;
+ data += (4 + (prtd->channel_mode ? 2048 : 1024));
+ MM_DBG("0x%8x\n", (int)(prtd->in[n].data - 8));
+ }
+ for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+ MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+
+ return audrec_send_audrecqueue(prtd, &cmd, sizeof(cmd));
+}
+
+int audio_dsp_out_enable(struct msm_audio *prtd, int yes)
+{
+ struct audpp_cmd_pcm_intf cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = AUDPP_CMD_PCM_INTF;
+ cmd.stream = AUDPP_CMD_POPP_STREAM;
+ cmd.stream_id = prtd->session_id;
+ cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V;
+ cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+
+ if (yes) {
+ cmd.write_buf1LSW = prtd->out[0].addr;
+ cmd.write_buf1MSW = prtd->out[0].addr >> 16;
+ cmd.write_buf1_len = prtd->out[0].size;
+ cmd.write_buf2LSW = prtd->out[1].addr;
+ cmd.write_buf2MSW = prtd->out[1].addr >> 16;
+ if (prtd->out[1].used)
+ cmd.write_buf2_len = prtd->out[1].used;
+ else
+ cmd.write_buf2_len = prtd->out[1].size;
+ cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V;
+ cmd.weight_decoder_to_rx = prtd->out_weight;
+ cmd.weight_arm_to_rx = 1;
+ cmd.partition_number_arm_to_dsp = 0;
+ cmd.sample_rate = prtd->out_sample_rate;
+ cmd.channel_mode = prtd->out_channel_mode;
+ }
+
+ return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+int alsa_buffer_read(struct msm_audio *prtd, void __user *buf,
+ size_t count, loff_t *pos)
+{
+ unsigned long flag;
+ void *data;
+ uint32_t index;
+ uint32_t size;
+ int ret = 0;
+
+ mutex_lock(&the_locks.read_lock);
+ while (count > 0) {
+ ret = wait_event_interruptible(the_locks.read_wait,
+ (prtd->in_count > 0)
+ || prtd->stopped ||
+ prtd->abort);
+
+ if (ret < 0)
+ break;
+
+ if (prtd->stopped) {
+ ret = -EBUSY;
+ break;
+ }
+
+ if (prtd->abort) {
+ MM_DBG(" prtd->abort !\n");
+ ret = -EPERM; /* Not permitted due to abort */
+ break;
+ }
+
+ index = prtd->in_tail;
+ data = (uint8_t *) prtd->in[index].data;
+ size = prtd->in[index].size;
+ if (count >= size) {
+ if (copy_to_user(buf, data, size)) {
+ ret = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
+ if (index != prtd->in_tail) {
+ /* overrun: data is invalid, we need to retry */
+ spin_unlock_irqrestore(&the_locks.read_dsp_lock,
+ flag);
+ continue;
+ }
+ prtd->in[index].size = 0;
+ prtd->in_tail = (prtd->in_tail + 1) & (FRAME_NUM - 1);
+ prtd->in_count--;
+ spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
+ count -= size;
+ buf += size;
+ } else {
+ break;
+ }
+ }
+ mutex_unlock(&the_locks.read_lock);
+ return ret;
+}
+EXPORT_SYMBOL(alsa_buffer_read);
+
+int alsa_dsp_send_buffer(struct msm_audio *prtd,
+ unsigned idx, unsigned len)
+{
+ struct audpp_cmd_pcm_intf_send_buffer cmd;
+ int i;
+ unsigned short *ptrmem = (unsigned short *)&cmd;
+
+ cmd.cmd_id = AUDPP_CMD_PCM_INTF;
+ cmd.stream = AUDPP_CMD_POPP_STREAM;
+ cmd.stream_id = prtd->session_id;
+ cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V;
+ cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+ cmd.dsp_to_arm_buf_id = 0;
+ cmd.arm_to_dsp_buf_id = idx + 1;
+ cmd.arm_to_dsp_buf_len = len;
+ for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+ MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+
+ return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static int alsa_dsp_read_buffer(struct msm_audio *audio, uint32_t read_cnt)
+{
+ struct up_audrec_packet_ext_ptr cmd;
+ int i;
+ unsigned short *ptrmem = (unsigned short *)&cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+ cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+ cmd.audrec_up_curr_read_count_lsw = read_cnt;
+ for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+ MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+
+ return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+
+static void alsa_get_dsp_frames(struct msm_audio *prtd)
+{
+ struct audio_frame *frame;
+ uint32_t index = 0;
+ unsigned long flag;
+
+ if (prtd->type == ENC_TYPE_WAV) {
+ index = prtd->in_head;
+
+ frame =
+ (void *)(((char *)prtd->in[index].data) - sizeof(*frame));
+
+ spin_lock_irqsave(&the_locks.read_dsp_lock, flag);
+ prtd->in[index].size = frame->bytes;
+ MM_DBG("frame = %08x\n", (unsigned int) frame);
+ MM_DBG("prtd->in[index].size = %08x\n",
+ (unsigned int) prtd->in[index].size);
+
+ prtd->in_head = (prtd->in_head + 1) & (FRAME_NUM - 1);
+
+ /* If overflow, move the tail index foward. */
+ if (prtd->in_head == prtd->in_tail)
+ prtd->in_tail = (prtd->in_tail + 1) & (FRAME_NUM - 1);
+ else
+ prtd->in_count++;
+
+ prtd->pcm_irq_pos += frame->bytes;
+ alsa_dsp_read_buffer(prtd, prtd->dsp_cnt++);
+ spin_unlock_irqrestore(&the_locks.read_dsp_lock, flag);
+
+ wake_up(&the_locks.read_wait);
+ }
+}
diff --git a/sound/soc/msm/msm7kv2-pcm.c b/sound/soc/msm/msm7kv2-pcm.c
new file mode 100644
index 0000000..c64a3ff
--- /dev/null
+++ b/sound/soc/msm/msm7kv2-pcm.c
@@ -0,0 +1,774 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.h>
+#include "msm7kv2-pcm.h"
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+#define HOSTPCM_STREAM_ID 5
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+int copy_count;
+
+static struct snd_pcm_hardware msm_pcm_playback_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = USE_FORMATS,
+ .rates = USE_RATE,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = MAX_BUFFER_PLAYBACK_SIZE,
+ .period_bytes_min = BUFSZ,
+ .period_bytes_max = BUFSZ,
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware msm_pcm_capture_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = USE_FORMATS,
+ .rates = USE_RATE,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = MAX_BUFFER_CAPTURE_SIZE,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 4096,
+ .periods_min = 4,
+ .periods_max = 4,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+static void alsa_out_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct msm_audio *prtd = (struct msm_audio *) private_data;
+ MM_DBG("evt_id = 0x%8x\n", evt_id);
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+ prtd->source |= (0x1 << evt_payload->routing_id);
+ if (prtd->running == 1 && prtd->enabled == 1)
+ audpp_route_stream(prtd->session_id, prtd->source);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+ prtd->source &= ~(0x1 << evt_payload->routing_id);
+ if (prtd->running == 1 && prtd->enabled == 1)
+ audpp_route_stream(prtd->session_id, prtd->source);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ prtd->vol_pan.volume = evt_payload->session_vol;
+ MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+ prtd->vol_pan.volume);
+ if (prtd->running)
+ audpp_set_volume_and_pan(prtd->session_id,
+ prtd->vol_pan.volume,
+ 0, POPP);
+ break;
+ default:
+ MM_DBG("Unknown Event\n");
+ break;
+ }
+}
+
+static void alsa_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct msm_audio *prtd = (struct msm_audio *) private_data;
+ MM_DBG("evt_id = 0x%8x\n", evt_id);
+
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY: {
+ MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+ prtd->source |= (0x1 << evt_payload->routing_id);
+
+ if ((prtd->running == 1) && (prtd->enabled == 1))
+ alsa_in_record_config(prtd, 1);
+
+ break;
+ }
+ case AUDDEV_EVT_DEV_RLS: {
+ MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+ prtd->source &= ~(0x1 << evt_payload->routing_id);
+
+ if (!prtd->running || !prtd->enabled)
+ break;
+
+ /* Turn off as per source */
+ if (prtd->source)
+ alsa_in_record_config(prtd, 1);
+ else
+ /* Turn off all */
+ alsa_in_record_config(prtd, 0);
+
+ break;
+ }
+ case AUDDEV_EVT_FREQ_CHG: {
+ MM_DBG("Encoder Driver got sample rate change event\n");
+ MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
+ MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
+ MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
+ if (prtd->running == 1) {
+ /* Stop Recording sample rate does not match
+ with device sample rate */
+ if (evt_payload->freq_info.sample_rate !=
+ prtd->samp_rate) {
+ alsa_in_record_config(prtd, 0);
+ prtd->abort = 1;
+ wake_up(&the_locks.read_wait);
+ }
+ }
+ break;
+ }
+ default:
+ MM_DBG("Unknown Event\n");
+ break;
+ }
+}
+
+static void msm_pcm_enqueue_data(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ unsigned int period_size;
+
+ MM_DBG("prtd->out_tail =%d mmap_flag=%d\n",
+ prtd->out_tail, prtd->mmap_flag);
+ period_size = snd_pcm_lib_period_bytes(substream);
+ alsa_dsp_send_buffer(prtd, prtd->out_tail, period_size);
+ prtd->out_tail ^= 1;
+ ++copy_count;
+ prtd->period++;
+ if (unlikely(prtd->period >= runtime->periods))
+ prtd->period = 0;
+
+}
+
+static void event_handler(void *data)
+{
+ struct msm_audio *prtd = data;
+ MM_DBG("\n");
+ snd_pcm_period_elapsed(prtd->substream);
+ if (prtd->mmap_flag) {
+ if (prtd->dir == SNDRV_PCM_STREAM_CAPTURE)
+ return;
+ if (!prtd->stopped)
+ msm_pcm_enqueue_data(prtd->substream);
+ else
+ prtd->out_needed++;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ MM_DBG("\n");
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ prtd->pcm_buf_pos = 0;
+ if (prtd->enabled)
+ return 0;
+
+ MM_DBG("\n");
+ /* rate and channels are sent to audio driver */
+ prtd->out_sample_rate = runtime->rate;
+ prtd->out_channel_mode = runtime->channels;
+ prtd->data = prtd->substream->dma_buffer.area;
+ prtd->phys = prtd->substream->dma_buffer.addr;
+ prtd->out[0].data = prtd->data + 0;
+ prtd->out[0].addr = prtd->phys + 0;
+ prtd->out[0].size = BUFSZ;
+ prtd->out[1].data = prtd->data + BUFSZ;
+ prtd->out[1].addr = prtd->phys + BUFSZ;
+ prtd->out[1].size = BUFSZ;
+
+ if (prtd->enabled | !(prtd->mmap_flag))
+ return 0;
+
+ prtd->out[0].used = prtd->pcm_count;
+ prtd->out[1].used = prtd->pcm_count;
+
+ mutex_lock(&the_locks.lock);
+ alsa_audio_configure(prtd);
+ mutex_unlock(&the_locks.lock);
+
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret = 0;
+ uint32_t freq;
+ MM_DBG("\n");
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ prtd->pcm_buf_pos = 0;
+
+ /* rate and channels are sent to audio driver */
+ prtd->type = ENC_TYPE_WAV;
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = (runtime->channels - 1);
+ prtd->buffer_size = prtd->channel_mode ? STEREO_DATA_SIZE : \
+ MONO_DATA_SIZE;
+
+ if (prtd->enabled)
+ return 0;
+
+ freq = prtd->samp_rate;
+
+ prtd->data = prtd->substream->dma_buffer.area;
+ prtd->phys = prtd->substream->dma_buffer.addr;
+ MM_DBG("prtd->data =%08x\n", (unsigned int)prtd->data);
+ MM_DBG("prtd->phys =%08x\n", (unsigned int)prtd->phys);
+
+ mutex_lock(&the_locks.lock);
+ ret = alsa_audio_configure(prtd);
+ mutex_unlock(&the_locks.lock);
+ if (ret)
+ return ret;
+ ret = wait_event_interruptible(the_locks.enable_wait,
+ prtd->running != 0);
+ MM_DBG("state prtd->running = %d ret = %d\n", prtd->running, ret);
+
+ if (prtd->running == 0)
+ ret = -ENODEV;
+ else
+ ret = 0;
+ prtd->enabled = 1;
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ unsigned long flag = 0;
+ int ret = 0;
+
+ MM_DBG("\n");
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ || !prtd->mmap_flag)
+ break;
+ if (!prtd->out_needed) {
+ prtd->stopped = 0;
+ break;
+ }
+ spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
+ if (prtd->running == 1) {
+ if (prtd->stopped == 1) {
+ prtd->stopped = 0;
+ prtd->period = 0;
+ if (prtd->pcm_irq_pos == 0) {
+ prtd->out_tail = 0;
+ msm_pcm_enqueue_data(prtd->substream);
+ prtd->out_needed--;
+ } else {
+ prtd->out_tail = 1;
+ msm_pcm_enqueue_data(prtd->substream);
+ prtd->out_needed--;
+ }
+ if (prtd->out_needed) {
+ prtd->out_tail ^= 1;
+ msm_pcm_enqueue_data(prtd->substream);
+ prtd->out_needed--;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ || !prtd->mmap_flag)
+ break;
+ prtd->stopped = 1;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+struct msm_audio_event_callbacks snd_msm_audio_ops = {
+ .playback = event_handler,
+ .capture = event_handler,
+};
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd;
+ int ret = 0;
+ int i = 0;
+ int session_attrb, sessionid;
+
+ MM_DBG("\n");
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (prtd->opened) {
+ kfree(prtd);
+ return -EBUSY;
+ }
+ runtime->hw = msm_pcm_playback_hardware;
+ prtd->dir = SNDRV_PCM_STREAM_PLAYBACK;
+ prtd->eos_ack = 0;
+ prtd->session_id = HOSTPCM_STREAM_ID;
+ prtd->device_events = AUDDEV_EVT_DEV_RDY |
+ AUDDEV_EVT_STREAM_VOL_CHG |
+ AUDDEV_EVT_DEV_RLS;
+ prtd->source = msm_snddev_route_dec(prtd->session_id);
+ MM_ERR("Register device event listener\n");
+ ret = auddev_register_evt_listner(prtd->device_events,
+ AUDDEV_CLNT_DEC, prtd->session_id,
+ alsa_out_listener, (void *) prtd);
+ if (ret) {
+ MM_ERR("failed to register device event listener\n");
+ goto evt_error;
+ }
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw = msm_pcm_capture_hardware;
+ prtd->dir = SNDRV_PCM_STREAM_CAPTURE;
+ session_attrb = ENC_TYPE_WAV;
+ sessionid = audpreproc_aenc_alloc(session_attrb,
+ &prtd->module_name, &prtd->queue_id);
+ if (sessionid < 0) {
+ MM_ERR("AUDREC not available\n");
+ kfree(prtd);
+ return -ENODEV;
+ }
+ prtd->session_id = sessionid;
+ MM_DBG("%s\n", prtd->module_name);
+ ret = msm_adsp_get(prtd->module_name, &prtd->audrec,
+ &alsa_audrec_adsp_ops, prtd);
+ if (ret < 0) {
+ audpreproc_aenc_free(prtd->session_id);
+ kfree(prtd);
+ return -ENODEV;
+ }
+
+ prtd->abort = 0;
+ prtd->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+ AUDDEV_EVT_FREQ_CHG;
+ prtd->source = msm_snddev_route_enc(prtd->session_id);
+ MM_ERR("Register device event listener\n");
+ ret = auddev_register_evt_listner(prtd->device_events,
+ AUDDEV_CLNT_ENC, prtd->session_id,
+ alsa_in_listener, (void *) prtd);
+ if (ret) {
+ MM_ERR("failed to register device event listener\n");
+ audpreproc_aenc_free(prtd->session_id);
+ msm_adsp_put(prtd->audrec);
+ goto evt_error;
+ }
+ }
+ prtd->substream = substream;
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ MM_ERR("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ MM_ERR("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->ops = &snd_msm_audio_ops;
+ prtd->out[0].used = BUF_INVALID_LEN;
+ prtd->out[1].used = 0;
+ prtd->out_head = 1; /* point to second buffer on startup */
+ prtd->out_tail = 0;
+ prtd->dsp_cnt = 0;
+ prtd->in_head = 0;
+ prtd->in_tail = 0;
+ prtd->in_count = 0;
+ prtd->out_needed = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ prtd->in[i].size = 0;
+ prtd->in[i].read = 0;
+ }
+ prtd->vol_pan.volume = 0x2000;
+ prtd->vol_pan.pan = 0x0;
+ prtd->opened = 1;
+ runtime->private_data = prtd;
+
+ copy_count = 0;
+ return 0;
+evt_error:
+ kfree(prtd);
+ return ret;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ MM_DBG("%d\n", fbytes);
+ ret = alsa_send_buffer(prtd, buf, fbytes, NULL);
+ ++copy_count;
+ prtd->pcm_buf_pos += fbytes;
+ if (copy_count == 1) {
+ mutex_lock(&the_locks.lock);
+ ret = alsa_audio_configure(prtd);
+ mutex_unlock(&the_locks.lock);
+ }
+ return ret;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ int ret = 0;
+
+ MM_DBG("\n");
+ if ((!prtd->mmap_flag) && prtd->enabled) {
+ ret = wait_event_interruptible(the_locks.eos_wait,
+ (!(prtd->out[0].used) && !(prtd->out[1].used)));
+
+ if (ret < 0)
+ goto done;
+ }
+
+ /* PCM DMAMISS message is sent only once in
+ * hpcm interface. So, wait for buffer complete
+ * and teos flag.
+ */
+ if (prtd->enabled)
+ ret = wait_event_interruptible(the_locks.eos_wait,
+ prtd->eos_ack);
+
+done:
+ alsa_audio_disable(prtd);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, prtd->session_id);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0, rc1 = 0, rc2 = 0;
+ int fbytes = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = substream->runtime->private_data;
+
+ int monofbytes = 0;
+ char *bufferp = NULL;
+
+ if (prtd->abort)
+ return -EPERM;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ MM_DBG("%d\n", fbytes);
+ monofbytes = fbytes / 2;
+ if (runtime->channels == 2) {
+ ret = alsa_buffer_read(prtd, buf, fbytes, NULL);
+ } else {
+ bufferp = buf;
+ rc1 = alsa_buffer_read(prtd, bufferp, monofbytes, NULL);
+ bufferp = buf + monofbytes ;
+ rc2 = alsa_buffer_read(prtd, bufferp, monofbytes, NULL);
+ ret = rc1 + rc2;
+ }
+ prtd->pcm_buf_pos += fbytes;
+ MM_DBG("prtd->pcm_buf_pos =%d, prtd->mmap_flag =%d\n",
+ prtd->pcm_buf_pos, prtd->mmap_flag);
+ return ret;
+}
+
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret = 0;
+
+ MM_DBG("\n");
+ ret = msm_snddev_withdraw_freq(prtd->session_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, prtd->session_id);
+ prtd->abort = 0;
+ wake_up(&the_locks.enable_wait);
+ alsa_audrec_disable(prtd);
+ audpreproc_aenc_free(prtd->session_id);
+ msm_adsp_put(prtd->audrec);
+ kfree(prtd);
+ return 0;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ MM_DBG("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+ if (prtd->pcm_irq_pos == prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ prtd->out_head = 0; /* point to First buffer on startup */
+ prtd->mmap_flag = 1;
+ runtime->dma_bytes = snd_pcm_lib_period_bytes(substream)*2;
+ dma_mmap_coherent(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return 0;
+}
+
+int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int pcm_preallocate_buffer(struct snd_pcm *pcm,
+ int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size;
+ if (!stream)
+ size = PLAYBACK_DMASZ;
+ else
+ size = CAPTURE_DMASZ;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_coherent(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+ return 0;
+}
+
+static void msm_pcm_free_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_coherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+static int msm_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+
+ ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 1);
+ if (ret)
+ return ret;
+ ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
+ if (ret)
+ return ret;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &msm_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &msm_pcm_ops);
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ret = pcm_preallocate_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ return ret;
+ ret = pcm_preallocate_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ msm_pcm_free_buffers(pcm);
+ return ret;
+}
+
+struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_pcm_new,
+ .pcm_free = msm_pcm_free_buffers,
+};
+EXPORT_SYMBOL(msm_soc_platform);
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .driver = {
+ .name = "msm-dsp-audio",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm7kv2-pcm.h b/sound/soc/msm/msm7kv2-pcm.h
new file mode 100644
index 0000000..fec7cf5
--- /dev/null
+++ b/sound/soc/msm/msm7kv2-pcm.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#ifndef _MSM_PCM_H
+#define _MSM_PCM_H
+
+
+#include <mach/qdsp5v2/qdsp5audppcmdi.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+
+
+#define FRAME_NUM (8)
+#define FRAME_SIZE (2052 * 2)
+#define MONO_DATA_SIZE (2048)
+#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2)
+#define CAPTURE_DMASZ (FRAME_SIZE * FRAME_NUM)
+
+#define BUFSZ (960 * 5)
+#define PLAYBACK_DMASZ (BUFSZ * 2)
+
+#define MSM_PLAYBACK_DEFAULT_VOLUME 0 /* 0dB */
+#define MSM_PLAYBACK_DEFAULT_PAN 0
+
+#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#define USE_CHANNELS_MIN 1
+#define USE_CHANNELS_MAX 2
+/* Support unconventional sample rates 12000, 24000 as well */
+#define USE_RATE \
+ (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+#define USE_RATE_MIN 8000
+#define USE_RATE_MAX 48000
+#define MAX_BUFFER_PLAYBACK_SIZE \
+ PLAYBACK_DMASZ
+/* 2048 frames (Mono), 1024 frames (Stereo) */
+#define CAPTURE_SIZE 4096
+#define MAX_BUFFER_CAPTURE_SIZE (4096*4)
+#define MAX_PERIOD_SIZE BUFSZ
+#define USE_PERIODS_MAX 1024
+#define USE_PERIODS_MIN 1
+
+
+#define MAX_DB (16)
+#define MIN_DB (-50)
+#define PCMPLAYBACK_DECODERID 5
+
+/* 0xFFFFFFFF Indicates not to be used for audio data copy */
+#define BUF_INVALID_LEN 0xFFFFFFFF
+#define EVENT_MSG_ID ((uint16_t)~0)
+
+#define AUDDEC_DEC_PCM 0
+/* Decoder status received from AUDPPTASK */
+#define AUDPP_DEC_STATUS_SLEEP 0
+#define AUDPP_DEC_STATUS_INIT 1
+#define AUDPP_DEC_STATUS_CFG 2
+#define AUDPP_DEC_STATUS_PLAY 3
+
+extern int copy_count;
+extern int intcnt;
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct buffer_rec {
+ void *data;
+ unsigned int size;
+ unsigned int read;
+ unsigned int addr;
+};
+
+struct audio_locks {
+ struct mutex lock;
+ struct mutex write_lock;
+ struct mutex read_lock;
+ spinlock_t read_dsp_lock;
+ spinlock_t write_dsp_lock;
+ spinlock_t mixer_lock;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t wait;
+ wait_queue_head_t eos_wait;
+ wait_queue_head_t enable_wait;
+};
+
+extern struct audio_locks the_locks;
+
+struct msm_audio_event_callbacks {
+ /* event is called from interrupt context when a message
+ * arrives from the DSP.
+ */
+ void (*playback)(void *);
+ void (*capture)(void *);
+};
+
+
+struct msm_audio {
+ struct buffer out[2];
+ struct buffer_rec in[8];
+
+ uint8_t out_head;
+ uint8_t out_tail;
+ uint8_t out_needed; /* number of buffers the dsp is waiting for */
+ atomic_t out_bytes;
+
+ /* configuration to use on next enable */
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ uint32_t out_weight;
+ uint32_t out_buffer_size;
+
+ struct snd_pcm_substream *substream;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ unsigned int pcm_buf_pos; /* position in buffer */
+ uint16_t source; /* Encoding source bit mask */
+
+ struct msm_adsp_module *audpre;
+ struct msm_adsp_module *audrec;
+ struct msm_adsp_module *audplay;
+ enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+
+ uint16_t session_id;
+ uint32_t out_bits; /* bits per sample */
+ const char *module_name;
+ unsigned queue_id;
+
+ /* configuration to use on next enable */
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */
+ uint32_t type; /* 0 for PCM ,1 for AAC */
+ uint32_t dsp_cnt;
+ uint32_t in_head; /* next buffer dsp will write */
+ uint32_t in_tail; /* next buffer read() will read */
+ uint32_t in_count; /* number of buffers available to read() */
+
+ unsigned short samp_rate_index;
+ uint32_t device_events; /* device events interested in */
+ int abort; /* set when error, like sample rate mismatch */
+
+ /* audpre settings */
+ /* For different sample rate, the coeff might be different. *
+ * All the coeff should be passed from user space */
+
+ struct msm_audio_event_callbacks *ops;
+
+ int dir;
+ int opened;
+ int enabled;
+ int running;
+ int stopped; /* set when stopped, cleared on flush */
+ int eos_ack;
+ int mmap_flag;
+ int period;
+ struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+
+
+/* platform data */
+extern int alsa_dsp_send_buffer(struct msm_audio *prtd,
+ unsigned idx, unsigned len);
+extern int audio_dsp_out_enable(struct msm_audio *prtd, int yes);
+extern struct snd_soc_platform_driver msm_soc_platform;
+
+extern int audrec_encoder_config(struct msm_audio *prtd);
+extern int alsa_audrec_disable(struct msm_audio *prtd);
+extern int alsa_audio_configure(struct msm_audio *prtd);
+extern int alsa_audio_disable(struct msm_audio *prtd);
+extern int alsa_buffer_read(struct msm_audio *prtd, void __user *buf,
+ size_t count, loff_t *pos);
+ssize_t alsa_send_buffer(struct msm_audio *prtd, const char __user *buf,
+ size_t count, loff_t *pos);
+extern struct msm_adsp_ops alsa_audrec_adsp_ops;
+extern int alsa_in_record_config(struct msm_audio *prtd, int enable);
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/msm7x30.c b/sound/soc/msm/msm7x30.c
new file mode 100644
index 0000000..94e37ca
--- /dev/null
+++ b/sound/soc/msm/msm7x30.c
@@ -0,0 +1,1004 @@
+/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio.h>
+
+#include "msm7kv2-pcm.h"
+#include <asm/mach-types.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/afe.h>
+
+static struct platform_device *msm_audio_snd_device;
+struct audio_locks the_locks;
+EXPORT_SYMBOL(the_locks);
+struct msm_volume msm_vol_ctl;
+EXPORT_SYMBOL(msm_vol_ctl);
+static struct snd_kcontrol_new snd_msm_controls[];
+
+char snddev_name[AUDIO_DEV_CTL_MAX_DEV][44];
+#define MSM_MAX_VOLUME 0x2000
+#define MSM_VOLUME_STEP ((MSM_MAX_VOLUME+17)/100) /* 17 added to avoid
+ more deviation */
+#define LOOPBACK_ENABLE 0x1
+#define LOOPBACK_DISABLE 0x0
+
+static int device_index; /* Count of Device controls */
+static int simple_control; /* Count of simple controls*/
+static int src_dev;
+static int dst_dev;
+static int loopback_status;
+
+
+static int msm_scontrol_count_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int msm_scontrol_count_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = simple_control;
+ return 0;
+}
+
+static int msm_v_call_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int msm_v_call_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_v_call_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int start = ucontrol->value.integer.value[0];
+ if (start)
+ broadcast_event(AUDDEV_EVT_START_VOICE, DEVICE_IGNORE,
+ SESSION_IGNORE);
+ else
+ broadcast_event(AUDDEV_EVT_END_VOICE, DEVICE_IGNORE,
+ SESSION_IGNORE);
+ return 0;
+}
+
+static int msm_v_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 2;
+ return 0;
+}
+
+static int msm_v_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_v_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int dir = ucontrol->value.integer.value[0];
+ int mute = ucontrol->value.integer.value[1];
+ return msm_set_voice_mute(dir, mute);
+}
+
+static int msm_v_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2; /* Volume */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ return 0;
+}
+
+static int msm_v_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_v_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int dir = ucontrol->value.integer.value[0];
+ int volume = ucontrol->value.integer.value[1];
+
+ return msm_set_voice_vol(dir, volume);
+}
+
+static int msm_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3; /* Volume and 10-base multiply factor*/
+ uinfo->value.integer.min = 0;
+
+ /* limit the muliply factor to 4 decimal digit */
+ uinfo->value.integer.max = 1000000;
+ return 0;
+}
+static int msm_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int session_id = ucontrol->value.integer.value[0];
+ int volume = ucontrol->value.integer.value[1];
+ int factor = ucontrol->value.integer.value[2];
+ u32 session_mask = 0;
+
+
+ if (factor > 10000)
+ return -EINVAL;
+
+ if ((volume < 0) || (volume/factor > 100))
+ return -EINVAL;
+
+ volume = (MSM_VOLUME_STEP * volume);
+
+ /* Convert back to original decimal point by removing the 10-base factor
+ * and discard the fractional portion
+ */
+
+ volume = volume/factor;
+
+ if (volume > MSM_MAX_VOLUME)
+ volume = MSM_MAX_VOLUME;
+
+ /* Only Decoder volume control supported */
+ session_mask = (0x1 << (session_id) << (8 * ((int)AUDDEV_CLNT_DEC-1)));
+ msm_vol_ctl.volume = volume;
+ MM_DBG("session_id %d, volume %d", session_id, volume);
+ broadcast_event(AUDDEV_EVT_STREAM_VOL_CHG, DEVICE_IGNORE,
+ session_mask);
+
+ return ret;
+}
+
+static int msm_voice_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3; /* Device */
+
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = msm_snddev_devcount();
+ return 0;
+}
+
+static int msm_voice_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ uint32_t rx_dev_id;
+ uint32_t tx_dev_id;
+ struct msm_snddev_info *rx_dev_info;
+ struct msm_snddev_info *tx_dev_info;
+ int set = ucontrol->value.integer.value[2];
+ u32 session_mask;
+
+ if (!set)
+ return -EPERM;
+ /* Rx Device Routing */
+ rx_dev_id = ucontrol->value.integer.value[0];
+ rx_dev_info = audio_dev_ctrl_find_dev(rx_dev_id);
+
+ if (IS_ERR(rx_dev_info)) {
+ MM_ERR("pass invalid dev_id\n");
+ rc = PTR_ERR(rx_dev_info);
+ return rc;
+ }
+
+ if (!(rx_dev_info->capability & SNDDEV_CAP_RX)) {
+ MM_ERR("First Dev is supposed to be RX\n");
+ return -EFAULT;
+ }
+
+ MM_DBG("route cfg %d STREAM_VOICE_RX type\n",
+ rx_dev_id);
+
+ msm_set_voc_route(rx_dev_info, AUDIO_ROUTE_STREAM_VOICE_RX,
+ rx_dev_id);
+
+ session_mask = 0x1 << (8 * ((int)AUDDEV_CLNT_VOC-1));
+
+ broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, rx_dev_id, session_mask);
+
+
+ /* Tx Device Routing */
+ tx_dev_id = ucontrol->value.integer.value[1];
+ tx_dev_info = audio_dev_ctrl_find_dev(tx_dev_id);
+
+ if (IS_ERR(tx_dev_info)) {
+ MM_ERR("pass invalid dev_id\n");
+ rc = PTR_ERR(tx_dev_info);
+ return rc;
+ }
+
+ if (!(tx_dev_info->capability & SNDDEV_CAP_TX)) {
+ MM_ERR("Second Dev is supposed to be Tx\n");
+ return -EFAULT;
+ }
+
+ MM_DBG("route cfg %d %d type\n",
+ tx_dev_id, AUDIO_ROUTE_STREAM_VOICE_TX);
+
+ msm_set_voc_route(tx_dev_info, AUDIO_ROUTE_STREAM_VOICE_TX,
+ tx_dev_id);
+
+ broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, tx_dev_id, session_mask);
+
+ if (rx_dev_info->opened)
+ broadcast_event(AUDDEV_EVT_DEV_RDY, rx_dev_id, session_mask);
+
+ if (tx_dev_info->opened)
+ broadcast_event(AUDDEV_EVT_DEV_RDY, tx_dev_id, session_mask);
+
+ return rc;
+}
+
+static int msm_voice_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ /* TODO: query Device list */
+ return 0;
+}
+
+static int msm_device_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1; /* Device */
+
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = msm_snddev_devcount();
+ return 0;
+}
+
+static int msm_device_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ int set = 0;
+ struct msm_audio_route_config route_cfg;
+ struct msm_snddev_info *dev_info;
+ struct msm_snddev_info *dst_dev_info;
+ struct msm_snddev_info *src_dev_info;
+ int tx_freq = 0;
+ int rx_freq = 0;
+ u32 set_freq = 0;
+
+ set = ucontrol->value.integer.value[0];
+ route_cfg.dev_id = ucontrol->id.numid - device_index;
+ dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
+ if (IS_ERR(dev_info)) {
+ MM_ERR("pass invalid dev_id\n");
+ rc = PTR_ERR(dev_info);
+ return rc;
+ }
+ MM_INFO("device %s set %d\n", dev_info->name, set);
+
+ if (set) {
+ if (!dev_info->opened) {
+ set_freq = dev_info->sample_rate;
+ if (!msm_device_is_voice(route_cfg.dev_id)) {
+ msm_get_voc_freq(&tx_freq, &rx_freq);
+ if (dev_info->capability & SNDDEV_CAP_TX)
+ set_freq = tx_freq;
+
+ if (set_freq == 0)
+ set_freq = dev_info->sample_rate;
+ } else
+ set_freq = dev_info->sample_rate;
+
+
+ MM_ERR("device freq =%d\n", set_freq);
+ rc = dev_info->dev_ops.set_freq(dev_info, set_freq);
+ if (rc < 0) {
+ MM_ERR("device freq failed!\n");
+ return rc;
+ }
+ dev_info->set_sample_rate = rc;
+ rc = 0;
+ rc = dev_info->dev_ops.open(dev_info);
+ if (rc < 0) {
+ MM_ERR("Enabling %s failed", dev_info->name);
+ return rc;
+ }
+ dev_info->opened = 1;
+ broadcast_event(AUDDEV_EVT_DEV_RDY, route_cfg.dev_id,
+ SESSION_IGNORE);
+ /* Event to notify client for device info */
+ broadcast_event(AUDDEV_EVT_DEVICE_INFO,
+ route_cfg.dev_id, SESSION_IGNORE);
+ if ((route_cfg.dev_id == src_dev) ||
+ (route_cfg.dev_id == dst_dev)) {
+ dst_dev_info = audio_dev_ctrl_find_dev(
+ dst_dev);
+ if (IS_ERR(dst_dev_info)) {
+ pr_err("dst_dev:%s:pass invalid"
+ "dev_id\n", __func__);
+ rc = PTR_ERR(dst_dev_info);
+ return rc;
+ }
+ src_dev_info = audio_dev_ctrl_find_dev(
+ src_dev);
+ if (IS_ERR(src_dev_info)) {
+ pr_err("src_dev:%s:pass invalid"
+ "dev_id\n", __func__);
+ rc = PTR_ERR(src_dev_info);
+ return rc;
+ }
+ if ((dst_dev_info->opened) &&
+ (src_dev_info->opened)) {
+ pr_debug("%d: Enable afe_loopback\n",
+ __LINE__);
+ afe_ext_loopback(LOOPBACK_ENABLE,
+ dst_dev_info->copp_id,
+ src_dev_info->copp_id);
+ loopback_status = 1;
+ }
+ }
+ }
+ } else {
+ if (dev_info->opened) {
+ broadcast_event(AUDDEV_EVT_REL_PENDING,
+ route_cfg.dev_id,
+ SESSION_IGNORE);
+ rc = dev_info->dev_ops.close(dev_info);
+ if (rc < 0) {
+ MM_ERR("Snd device failed close!\n");
+ return rc;
+ } else {
+ dev_info->opened = 0;
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ route_cfg.dev_id,
+ SESSION_IGNORE);
+ }
+ if (loopback_status == 1) {
+ if ((route_cfg.dev_id == src_dev) ||
+ (route_cfg.dev_id == dst_dev)) {
+ dst_dev_info = audio_dev_ctrl_find_dev(
+ dst_dev);
+ if (IS_ERR(dst_dev_info)) {
+ pr_err("dst_dev:%s:pass invalid"
+ "dev_id\n", __func__);
+ rc = PTR_ERR(dst_dev_info);
+ return rc;
+ }
+ src_dev_info = audio_dev_ctrl_find_dev(
+ src_dev);
+ if (IS_ERR(src_dev_info)) {
+ pr_err("dst_dev:%s:pass invalid"
+ "dev_id\n", __func__);
+ rc = PTR_ERR(src_dev_info);
+ return rc;
+ }
+ pr_debug("%d: Disable afe_loopback\n",
+ __LINE__);
+ afe_ext_loopback(LOOPBACK_DISABLE,
+ dst_dev_info->copp_id,
+ src_dev_info->copp_id);
+ loopback_status = 0;
+ }
+ }
+ }
+
+ }
+ return rc;
+}
+
+static int msm_device_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct msm_audio_route_config route_cfg;
+ struct msm_snddev_info *dev_info;
+
+ route_cfg.dev_id = ucontrol->id.numid - device_index;
+ dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
+
+ if (IS_ERR(dev_info)) {
+ MM_ERR("pass invalid dev_id\n");
+ rc = PTR_ERR(dev_info);
+ return rc;
+ }
+
+ ucontrol->value.integer.value[0] = dev_info->copp_id;
+ ucontrol->value.integer.value[1] = dev_info->capability;
+
+ return 0;
+}
+
+static int msm_route_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3; /* Device */
+
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = msm_snddev_devcount();
+ return 0;
+}
+
+static int msm_route_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ /* TODO: query Device list */
+ return 0;
+}
+
+static int msm_route_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ int enc_freq = 0;
+ int requested_freq = 0;
+ struct msm_audio_route_config route_cfg;
+ struct msm_snddev_info *dev_info;
+ int session_id = ucontrol->value.integer.value[0];
+ int set = ucontrol->value.integer.value[2];
+ u32 session_mask = 0;
+ route_cfg.dev_id = ucontrol->value.integer.value[1];
+
+ if (ucontrol->id.numid == 2)
+ route_cfg.stream_type = AUDIO_ROUTE_STREAM_PLAYBACK;
+ else
+ route_cfg.stream_type = AUDIO_ROUTE_STREAM_REC;
+
+ MM_DBG("route cfg %d %d type for popp %d set value %d\n",
+ route_cfg.dev_id, route_cfg.stream_type, session_id, set);
+ dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
+
+ if (IS_ERR(dev_info)) {
+ MM_ERR("pass invalid dev_id\n");
+ rc = PTR_ERR(dev_info);
+ return rc;
+ }
+ if (route_cfg.stream_type == AUDIO_ROUTE_STREAM_PLAYBACK) {
+ rc = msm_snddev_set_dec(session_id, dev_info->copp_id, set);
+ session_mask =
+ (0x1 << (session_id) << (8 * ((int)AUDDEV_CLNT_DEC-1)));
+ if (!set) {
+ if (dev_info->opened) {
+ broadcast_event(AUDDEV_EVT_REL_PENDING,
+ route_cfg.dev_id,
+ session_mask);
+
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ route_cfg.dev_id,
+ session_mask);
+ }
+ dev_info->sessions &= ~(session_mask);
+ } else {
+ dev_info->sessions = dev_info->sessions | session_mask;
+ if (dev_info->opened) {
+ broadcast_event(AUDDEV_EVT_DEV_RDY,
+ route_cfg.dev_id,
+ session_mask);
+ /* Event to notify client for device info */
+ broadcast_event(AUDDEV_EVT_DEVICE_INFO,
+ route_cfg.dev_id,
+ session_mask);
+ }
+ }
+ } else {
+ rc = msm_snddev_set_enc(session_id, dev_info->copp_id, set);
+ session_mask =
+ (0x1 << (session_id)) << (8 * ((int)AUDDEV_CLNT_ENC-1));
+ if (!set) {
+ if (dev_info->opened)
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ route_cfg.dev_id,
+ session_mask);
+ dev_info->sessions &= ~(session_mask);
+ } else {
+ dev_info->sessions = dev_info->sessions | session_mask;
+ enc_freq = msm_snddev_get_enc_freq(session_id);
+ requested_freq = enc_freq;
+ if (enc_freq > 0) {
+ rc = msm_snddev_request_freq(&enc_freq,
+ session_id,
+ SNDDEV_CAP_TX,
+ AUDDEV_CLNT_ENC);
+ MM_DBG("sample rate configured %d"
+ "sample rate requested %d\n",
+ enc_freq, requested_freq);
+ if ((rc <= 0) || (enc_freq != requested_freq)) {
+ MM_DBG("msm_snddev_withdraw_freq\n");
+ rc = msm_snddev_withdraw_freq
+ (session_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ broadcast_event(AUDDEV_EVT_FREQ_CHG,
+ route_cfg.dev_id,
+ SESSION_IGNORE);
+ }
+ }
+ if (dev_info->opened) {
+ broadcast_event(AUDDEV_EVT_DEV_RDY,
+ route_cfg.dev_id,
+ session_mask);
+ /* Event to notify client for device info */
+ broadcast_event(AUDDEV_EVT_DEVICE_INFO,
+ route_cfg.dev_id,
+ session_mask);
+ }
+ }
+ }
+
+ if (rc < 0) {
+ MM_ERR("device could not be assigned!\n");
+ return -EFAULT;
+ }
+
+ return rc;
+}
+
+static int msm_device_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ return 0;
+}
+
+static int msm_device_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_snddev_info *dev_info;
+
+ int dev_id = ucontrol->value.integer.value[0];
+
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+ ucontrol->value.integer.value[0] = dev_info->dev_volume;
+
+ return 0;
+}
+
+static int msm_device_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = -EPERM;
+ struct msm_snddev_info *dev_info;
+
+ int dev_id = ucontrol->value.integer.value[0];
+ int volume = ucontrol->value.integer.value[1];
+
+ MM_DBG("dev_id = %d, volume = %d\n", dev_id, volume);
+
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+
+ if (IS_ERR(dev_info)) {
+ rc = PTR_ERR(dev_info);
+ MM_ERR("audio_dev_ctrl_find_dev failed. %ld\n",
+ PTR_ERR(dev_info));
+ return rc;
+ }
+
+ MM_DBG("dev_name = %s dev_id = %d, volume = %d\n",
+ dev_info->name, dev_id, volume);
+
+ if (dev_info->dev_ops.set_device_volume)
+ rc = dev_info->dev_ops.set_device_volume(dev_info, volume);
+ else {
+ MM_INFO("device %s does not support device volume "
+ "control.", dev_info->name);
+ return -EPERM;
+ }
+
+ return rc;
+}
+
+static int msm_reset_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0;
+ return 0;
+}
+
+static int msm_reset_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_reset_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ MM_DBG("Resetting all devices\n");
+ return msm_reset_all_device();
+}
+
+
+static int msm_dual_mic_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ /*Max value is decided based on MAX ENC sessions*/
+ uinfo->value.integer.max = MAX_AUDREC_SESSIONS - 1;
+ return 0;
+}
+
+static int msm_dual_mic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int enc_session_id = ucontrol->value.integer.value[0];
+ ucontrol->value.integer.value[1] =
+ msm_get_dual_mic_config(enc_session_id);
+ MM_DBG("session id = %d, config = %ld\n", enc_session_id,
+ ucontrol->value.integer.value[1]);
+ return 0;
+}
+
+static int msm_dual_mic_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int enc_session_id = ucontrol->value.integer.value[0];
+ int dual_mic_config = ucontrol->value.integer.value[1];
+ MM_DBG("session id = %d, config = %d\n", enc_session_id,
+ dual_mic_config);
+ return msm_set_dual_mic_config(enc_session_id, dual_mic_config);
+}
+
+static int msm_device_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = msm_snddev_devcount();
+ return 0;
+}
+
+static int msm_device_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int msm_device_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int dev_id = ucontrol->value.integer.value[0];
+ int mute = ucontrol->value.integer.value[1];
+ struct msm_snddev_info *dev_info;
+ int afe_dev_id = 0;
+ int volume = 0x4000;
+
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+ if (IS_ERR(dev_info)) {
+ MM_ERR("pass invalid dev_id %d\n", dev_id);
+ return PTR_ERR(dev_info);
+ }
+
+ if (dev_info->capability & SNDDEV_CAP_RX)
+ return -EPERM;
+
+ MM_DBG("Muting device id %d(%s)\n", dev_id, dev_info->name);
+
+ if (dev_info->copp_id == 0)
+ afe_dev_id = AFE_HW_PATH_CODEC_TX;
+ if (dev_info->copp_id == 1)
+ afe_dev_id = AFE_HW_PATH_AUXPCM_TX;
+ if (dev_info->copp_id == 2)
+ afe_dev_id = AFE_HW_PATH_MI2S_TX;
+ if (mute)
+ volume = 0;
+ afe_device_volume_ctrl(afe_dev_id, volume);
+ return 0;
+}
+
+static int msm_loopback_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = msm_snddev_devcount();
+ return 0;
+}
+
+static int msm_loopback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_loopback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct msm_snddev_info *src_dev_info = NULL; /* TX device */
+ struct msm_snddev_info *dst_dev_info = NULL; /* RX device */
+ int dst_dev_id = ucontrol->value.integer.value[0];
+ int src_dev_id = ucontrol->value.integer.value[1];
+ int set = ucontrol->value.integer.value[2];
+
+ pr_debug("%s: set=%d\n", __func__, set);
+
+ dst_dev_info = audio_dev_ctrl_find_dev(dst_dev_id);
+ if (IS_ERR(dst_dev_info)) {
+ pr_err("dst_dev:%s:pass invalid dev_id\n", __func__);
+ rc = PTR_ERR(dst_dev_info);
+ return rc;
+ }
+ if (!(dst_dev_info->capability & SNDDEV_CAP_RX)) {
+ pr_err("Destination device %d is not RX device\n",
+ dst_dev_id);
+ return -EFAULT;
+ }
+
+ src_dev_info = audio_dev_ctrl_find_dev(src_dev_id);
+ if (IS_ERR(src_dev_info)) {
+ pr_err("src_dev:%s:pass invalid dev_id\n", __func__);
+ rc = PTR_ERR(src_dev_info);
+ return rc;
+ }
+ if (!(src_dev_info->capability & SNDDEV_CAP_TX)) {
+ pr_err("Source device %d is not TX device\n", src_dev_id);
+ return -EFAULT;
+ }
+
+ if (set) {
+ pr_debug("%s:%d:Enabling AFE_Loopback\n", __func__, __LINE__);
+ src_dev = src_dev_id;
+ dst_dev = dst_dev_id;
+ loopback_status = 1;
+ if ((dst_dev_info->opened) && (src_dev_info->opened))
+ afe_ext_loopback(LOOPBACK_ENABLE,
+ dst_dev_info->copp_id,
+ src_dev_info->copp_id);
+ } else {
+ pr_debug("%s:%d:Disabling AFE_Loopback\n", __func__, __LINE__);
+ src_dev = DEVICE_IGNORE;
+ dst_dev = DEVICE_IGNORE;
+ loopback_status = 0;
+ afe_ext_loopback(LOOPBACK_DISABLE,
+ dst_dev_info->copp_id,
+ src_dev_info->copp_id);
+ }
+ return 0;
+}
+
+static struct snd_kcontrol_new snd_dev_controls[AUDIO_DEV_CTL_MAX_DEV];
+
+static int snd_dev_ctl_index(int idx)
+{
+ struct msm_snddev_info *dev_info;
+
+ dev_info = audio_dev_ctrl_find_dev(idx);
+ if (IS_ERR(dev_info)) {
+ MM_ERR("pass invalid dev_id\n");
+ return PTR_ERR(dev_info);
+ }
+ if (sizeof(dev_info->name) <= 44)
+ sprintf(&snddev_name[idx][0] , "%s", dev_info->name);
+
+ snd_dev_controls[idx].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ snd_dev_controls[idx].access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ snd_dev_controls[idx].name = &snddev_name[idx][0];
+ snd_dev_controls[idx].index = idx;
+ snd_dev_controls[idx].info = msm_device_info;
+ snd_dev_controls[idx].get = msm_device_get;
+ snd_dev_controls[idx].put = msm_device_put;
+ snd_dev_controls[idx].private_value = 0;
+ return 0;
+
+}
+
+#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .name = xname, \
+ .info = fp_info,\
+ .get = fp_get, .put = fp_put, \
+ .private_value = addr, \
+}
+
+static struct snd_kcontrol_new snd_msm_controls[] = {
+ MSM_EXT("Count", msm_scontrol_count_info, msm_scontrol_count_get, \
+ NULL, 0),
+ MSM_EXT("Stream", msm_route_info, msm_route_get, \
+ msm_route_put, 0),
+ MSM_EXT("Record", msm_route_info, msm_route_get, \
+ msm_route_put, 0),
+ MSM_EXT("Voice", msm_voice_info, msm_voice_get, \
+ msm_voice_put, 0),
+ MSM_EXT("Volume", msm_volume_info, msm_volume_get, \
+ msm_volume_put, 0),
+ MSM_EXT("VoiceVolume", msm_v_volume_info, msm_v_volume_get, \
+ msm_v_volume_put, 0),
+ MSM_EXT("VoiceMute", msm_v_mute_info, msm_v_mute_get, \
+ msm_v_mute_put, 0),
+ MSM_EXT("Voice Call", msm_v_call_info, msm_v_call_get, \
+ msm_v_call_put, 0),
+ MSM_EXT("Device_Volume", msm_device_volume_info,
+ msm_device_volume_get, msm_device_volume_put, 0),
+ MSM_EXT("Reset", msm_reset_info,
+ msm_reset_get, msm_reset_put, 0),
+ MSM_EXT("DualMic Switch", msm_dual_mic_info,
+ msm_dual_mic_get, msm_dual_mic_put, 0),
+ MSM_EXT("Device_Mute", msm_device_mute_info,
+ msm_device_mute_get, msm_device_mute_put, 0),
+ MSM_EXT("Sound Device Loopback", msm_loopback_info,
+ msm_loopback_get, msm_loopback_put, 0),
+};
+
+static int msm_new_mixer(struct snd_soc_codec *codec)
+{
+ unsigned int idx;
+ int err;
+ int dev_cnt;
+
+ strcpy(codec->card->snd_card->mixername, "MSM Mixer");
+ for (idx = 0; idx < ARRAY_SIZE(snd_msm_controls); idx++) {
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&snd_msm_controls[idx], NULL));
+ if (err < 0)
+ MM_ERR("ERR adding ctl\n");
+ }
+ dev_cnt = msm_snddev_devcount();
+
+ for (idx = 0; idx < dev_cnt; idx++) {
+ if (!snd_dev_ctl_index(idx)) {
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&snd_dev_controls[idx], NULL));
+ if (err < 0)
+ MM_ERR("ERR adding ctl\n");
+ } else
+ return 0;
+ }
+ simple_control = ARRAY_SIZE(snd_msm_controls);
+ device_index = simple_control + 1;
+ return 0;
+}
+
+static int msm_soc_dai_init(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ struct snd_soc_codec *codec = rtd->codec;
+ ret = msm_new_mixer(codec);
+ if (ret < 0)
+ MM_ERR("msm_soc: ALSA MSM Mixer Fail\n");
+
+ mutex_init(&the_locks.lock);
+ mutex_init(&the_locks.write_lock);
+ mutex_init(&the_locks.read_lock);
+ spin_lock_init(&the_locks.read_dsp_lock);
+ spin_lock_init(&the_locks.write_dsp_lock);
+ spin_lock_init(&the_locks.mixer_lock);
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+ src_dev = DEVICE_IGNORE;
+ dst_dev = DEVICE_IGNORE;
+
+ return ret;
+}
+
+static struct snd_soc_dai_link msm_dai[] = {
+{
+ .name = "MSM Primary I2S",
+ .stream_name = "DSP 1",
+ .cpu_dai_name = "msm-cpu-dai.0",
+ .platform_name = "msm-dsp-audio.0",
+ .codec_name = "msm-codec-dai.0",
+ .codec_dai_name = "msm-codec-dai",
+ .init = &msm_soc_dai_init,
+},
+#ifdef CONFIG_SND_MVS_SOC
+{
+ .name = "MSM Primary Voip",
+ .stream_name = "MVS",
+ .cpu_dai_name = "mvs-cpu-dai.0",
+ .platform_name = "msm-mvs-audio.0",
+ .codec_name = "mvs-codec-dai.0",
+ .codec_dai_name = "mvs-codec-dai",
+},
+#endif
+};
+
+static struct snd_soc_card snd_soc_card_msm = {
+ .name = "msm-audio",
+ .dai_link = msm_dai,
+ .num_links = ARRAY_SIZE(msm_dai),
+};
+
+static int __init msm_audio_init(void)
+{
+ int ret;
+
+ msm_audio_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!msm_audio_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(msm_audio_snd_device, &snd_soc_card_msm);
+ ret = platform_device_add(msm_audio_snd_device);
+ if (ret) {
+ platform_device_put(msm_audio_snd_device);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void __exit msm_audio_exit(void)
+{
+ platform_device_unregister(msm_audio_snd_device);
+}
+
+module_init(msm_audio_init);
+module_exit(msm_audio_exit);
+
+MODULE_DESCRIPTION("PCM module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm8660-dma.c b/sound/soc/msm/msm8660-dma.c
new file mode 100644
index 0000000..8ab429a
--- /dev/null
+++ b/sound/soc/msm/msm8660-dma.c
@@ -0,0 +1,432 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/android_pmem.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <mach/msm_iomap-8x60.h>
+#include <mach/audio_dma_msm8k.h>
+#include <sound/dai.h>
+#include "msm8660-pcm.h"
+
+struct dai_baseinfo {
+ void __iomem *base;
+};
+
+static struct dai_baseinfo dai_info;
+
+struct dai_drv {
+ u8 *buffer;
+ u32 buffer_phys;
+ int channels;
+ irqreturn_t (*callback) (int intrsrc, void *private_data);
+ void *private_data;
+ int in_use;
+ u32 buffer_len;
+ u32 period_len;
+ u32 master_mode;
+};
+
+static struct dai_drv *dai[MAX_CHANNELS];
+static spinlock_t dai_lock;
+
+static int dai_find_dma_channel(uint32_t intrsrc)
+{
+ int i, dma_channel = 0;
+ pr_debug("%s\n", __func__);
+
+ for (i = 0; i <= 27; i += 3) {
+ if (intrsrc & (1 << i)) {
+ dma_channel = i / 3;
+ break;
+ }
+ }
+ return dma_channel;
+}
+
+void register_dma_irq_handler(int dma_ch,
+ irqreturn_t (*callback) (int intrsrc, void *private_data),
+ void *private_data)
+{
+ pr_debug("%s\n", __func__);
+ dai[dma_ch]->callback = callback;
+ dai[dma_ch]->private_data = private_data;
+}
+
+void unregister_dma_irq_handler(int dma_ch)
+{
+ pr_debug("%s\n", __func__);
+ dai[dma_ch]->callback = NULL;
+ dai[dma_ch]->private_data = NULL;
+}
+
+static irqreturn_t dai_irq_handler(int irq, void *data)
+{
+ unsigned long flag;
+ uint32_t intrsrc;
+ uint32_t dma_ch = 0;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&dai_lock, flag);
+ intrsrc = readl(dai_info.base + LPAIF_IRQ_STAT(0));
+ writel(intrsrc, dai_info.base + LPAIF_IRQ_CLEAR(0));
+ while (intrsrc) {
+ dma_ch = dai_find_dma_channel(intrsrc);
+
+ if (!dai[dma_ch]->callback)
+ goto handled;
+ if (!dai[dma_ch]->private_data)
+ goto handled;
+ ret = dai[dma_ch]->callback(intrsrc,
+ dai[dma_ch]->private_data);
+ intrsrc &= ~(0x7 << (dma_ch * 3));
+ }
+handled:
+ spin_unlock_irqrestore(&dai_lock, flag);
+ return ret;
+}
+
+void dai_print_state(uint32_t dma_ch)
+{
+ int i = 0;
+ unsigned long *ptrmem = (unsigned long *)dai_info.base;
+
+ for (i = 0; i < 4; i++, ++ptrmem)
+ pr_debug("[0x%08x]=0x%08x\n", (unsigned int)ptrmem,
+ (unsigned int)*ptrmem);
+
+ ptrmem = (unsigned long *)(dai_info.base
+ + DMA_CH_CTL_BASE + DMA_CH_INDEX(dma_ch));
+ for (i = 0; i < 10; i++, ++ptrmem)
+ pr_debug("[0x%08x]=0x%08x\n", (unsigned int)ptrmem,
+ (unsigned int) *ptrmem);
+}
+
+static int dai_enable_irq(uint32_t dma_ch)
+{
+ int ret;
+ pr_debug("%s\n", __func__);
+ ret = request_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, dai_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_SHARED, "msm-i2s",
+ (void *) (dma_ch+1));
+ if (ret < 0) {
+ pr_debug("Request Irq Failed err = %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static void dai_config_dma(uint32_t dma_ch)
+{
+ pr_debug("%s dma_ch = %u\n", __func__, dma_ch);
+
+ writel(dai[dma_ch]->buffer_phys,
+ dai_info.base + LPAIF_DMA_BASE(dma_ch));
+ writel(((dai[dma_ch]->buffer_len >> 2) - 1),
+ dai_info.base + LPAIF_DMA_BUFF_LEN(dma_ch));
+ writel(((dai[dma_ch]->period_len >> 2) - 1),
+ dai_info.base + LPAIF_DMA_PER_LEN(dma_ch));
+}
+
+static void dai_enable_codec(uint32_t dma_ch, int codec)
+{
+ uint32_t intrVal;
+ uint32_t i2sctl;
+ pr_debug("%s\n", __func__);
+
+ intrVal = readl(dai_info.base + LPAIF_IRQ_EN(0));
+ intrVal = intrVal | (7 << (dma_ch * 3));
+ writel(intrVal, dai_info.base + LPAIF_IRQ_EN(0));
+ if (codec == DAI_SPKR) {
+ writel(0x0813, dai_info.base + LPAIF_DMA_CTL(dma_ch));
+ i2sctl = 0x4400;
+ i2sctl |= (dai[dma_ch]->master_mode ? WS_SRC_INT : WS_SRC_EXT);
+ writel(i2sctl, dai_info.base + LPAIF_I2S_CTL_OFFSET(DAI_SPKR));
+ } else if (codec == DAI_MIC) {
+ writel(0x81b, dai_info.base + LPAIF_DMA_CTL(dma_ch));
+ i2sctl = 0x0110;
+ i2sctl |= (dai[dma_ch]->master_mode ? WS_SRC_INT : WS_SRC_EXT);
+ writel(i2sctl, dai_info.base + LPAIF_I2S_CTL_OFFSET(DAI_MIC));
+ }
+}
+
+static void dai_disable_codec(uint32_t dma_ch, int codec)
+{
+ uint32_t intrVal = 0;
+ uint32_t intrVal1 = 0;
+ unsigned long flag = 0x0;
+
+ pr_debug("%s\n", __func__);
+ spin_lock_irqsave(&dai_lock, flag);
+
+ intrVal1 = readl(dai_info.base + LPAIF_I2S_CTL_OFFSET(codec));
+
+ if (codec == DAI_SPKR)
+ intrVal1 = intrVal1 & ~(1 << 14);
+ else if (codec == DAI_MIC)
+ intrVal1 = intrVal1 & ~(1 << 8);
+
+ writel(intrVal1, dai_info.base + LPAIF_I2S_CTL_OFFSET(codec));
+ intrVal = 0x0;
+ writel(intrVal, dai_info.base + LPAIF_DMA_CTL(dma_ch));
+
+ spin_unlock_irqrestore(&dai_lock, flag);
+}
+
+int dai_open(uint32_t dma_ch)
+{
+
+ pr_debug("%s\n", __func__);
+ if (!dai_info.base) {
+ pr_debug("%s failed as no msm-dai device\n", __func__);
+ return -ENODEV;
+ }
+ if (dma_ch >= MAX_CHANNELS) {
+ pr_debug("%s over max channesl %d\n", __func__, dma_ch);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+void dai_close(uint32_t dma_ch)
+{
+ pr_debug("%s\n", __func__);
+ if ((dma_ch >= 0) && (dma_ch < 5))
+ dai_disable_codec(dma_ch, DAI_SPKR);
+ else
+ dai_disable_codec(dma_ch, DAI_MIC);
+ free_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, (void *) (dma_ch + 1));
+}
+
+void dai_set_master_mode(uint32_t dma_ch, int mode)
+{
+ if (dma_ch < MAX_CHANNELS)
+ dai[dma_ch]->master_mode = mode;
+ else
+ pr_err("%s: invalid dma channel\n", __func__);
+}
+
+int dai_set_params(uint32_t dma_ch, struct dai_dma_params *params)
+{
+ pr_debug("%s\n", __func__);
+ dai[dma_ch]->buffer = params->buffer;
+ dai[dma_ch]->buffer_phys = params->src_start;
+ dai[dma_ch]->channels = params->channels;
+ dai[dma_ch]->buffer_len = params->buffer_size;
+ dai[dma_ch]->period_len = params->period_size;
+ dai_config_dma(dma_ch);
+ return dma_ch;
+}
+
+int dai_start(uint32_t dma_ch)
+{
+ unsigned long flag = 0x0;
+
+ spin_lock_irqsave(&dai_lock, flag);
+ dai_enable_irq(dma_ch);
+ if ((dma_ch >= 0) && (dma_ch < 5))
+ dai_enable_codec(dma_ch, DAI_SPKR);
+ else
+ dai_enable_codec(dma_ch, DAI_MIC);
+ spin_unlock_irqrestore(&dai_lock, flag);
+ dai_print_state(dma_ch);
+ return 0;
+}
+
+#define HDMI_BURST_INCR4 (1 << 11)
+#define HDMI_WPSCNT (1 << 8)
+#define HDMI_AUDIO_INTF (5 << 4)
+#define HDMI_FIFO_WATER_MARK (7 << 1)
+#define HDMI_ENABLE (1)
+
+int dai_start_hdmi(uint32_t dma_ch)
+{
+ unsigned long flag = 0x0;
+ uint32_t val;
+
+ pr_debug("%s dma_ch = %u\n", __func__, dma_ch);
+
+ spin_lock_irqsave(&dai_lock, flag);
+
+ dai_enable_irq(dma_ch);
+
+ if ((dma_ch >= 0) && (dma_ch < 5)) {
+
+ val = readl(dai_info.base + LPAIF_IRQ_EN(0));
+ val = val | (7 << (dma_ch * 3));
+ writel(val, dai_info.base + LPAIF_IRQ_EN(0));
+
+
+ val = (HDMI_BURST_INCR4 | HDMI_WPSCNT | HDMI_AUDIO_INTF |
+ HDMI_FIFO_WATER_MARK | HDMI_ENABLE);
+
+ writel(val, dai_info.base + LPAIF_DMA_CTL(dma_ch));
+ }
+ spin_unlock_irqrestore(&dai_lock, flag);
+
+ dai_print_state(dma_ch);
+ return 0;
+}
+
+void dai_stop_hdmi(uint32_t dma_ch)
+{
+ unsigned long flag = 0x0;
+ uint32_t intrVal;
+ uint32_t int_mask = 0x00000007;
+
+ pr_debug("%s dma_ch %u\n", __func__, dma_ch);
+
+ spin_lock_irqsave(&dai_lock, flag);
+
+ free_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, (void *) (dma_ch + 1));
+
+
+ intrVal = 0x0;
+ writel(intrVal, dai_info.base + LPAIF_DMA_CTL(dma_ch));
+
+ intrVal = readl(dai_info.base + LPAIF_IRQ_EN(0));
+
+ int_mask = ((int_mask) << (dma_ch * 3));
+ int_mask = ~int_mask;
+
+ intrVal = intrVal && int_mask;
+ writel(intrVal, dai_info.base + LPAIF_IRQ_EN(0));
+
+ spin_unlock_irqrestore(&dai_lock, flag);
+}
+
+int dai_stop(uint32_t dma_ch)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+
+uint32_t dai_get_dma_pos(uint32_t dma_ch)
+{
+
+ uint32_t addr;
+
+ pr_debug("%s\n", __func__);
+ addr = readl(dai_info.base + LPAIF_DMA_CURR_ADDR(dma_ch));
+
+ return addr;
+}
+
+static int __devinit dai_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ int i = 0;
+ struct resource *src;
+ src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msm-dai");
+ if (!src) {
+ rc = -ENODEV;
+ pr_debug("%s Error rc=%d\n", __func__, rc);
+ goto error;
+ }
+ for (i = 0; i <= MAX_CHANNELS; i++) {
+ dai[i] = kzalloc(sizeof(struct dai_drv), GFP_KERNEL);
+ if (!dai[0]) {
+ pr_debug("Allocation failed for dma_channel = 0\n");
+ return -ENODEV;
+ }
+ }
+ dai_info.base = ioremap(src->start, (src->end - src->start) + 1);
+ pr_debug("%s: msm-dai: 0x%08x\n", __func__,
+ (unsigned int)dai_info.base);
+ spin_lock_init(&dai_lock);
+error:
+ return rc;
+}
+
+static int dai_remove(struct platform_device *pdev)
+{
+ iounmap(dai_info.base);
+ return 0;
+}
+
+static struct platform_driver dai_driver = {
+ .probe = dai_probe,
+ .remove = dai_remove,
+ .driver = {
+ .name = "msm-dai",
+ .owner = THIS_MODULE
+ },
+};
+
+static struct resource msm_lpa_resources[] = {
+ {
+ .start = MSM_LPA_PHYS,
+ .end = MSM_LPA_END,
+ .flags = IORESOURCE_MEM,
+ .name = "msm-dai",
+ },
+};
+
+static struct platform_device *codec_device;
+
+static int msm_dai_dev_register(const char *name)
+{
+ int ret = 0;
+
+ pr_debug("%s : called\n", __func__);
+ codec_device = platform_device_alloc(name, -1);
+ if (codec_device == NULL) {
+ pr_debug("Failed to allocate %s\n", name);
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(codec_device, (void *)&dai_info);
+ platform_device_add_resources(codec_device, &msm_lpa_resources[0],
+ ARRAY_SIZE(msm_lpa_resources));
+ ret = platform_device_add(codec_device);
+ if (ret != 0) {
+ pr_debug("Failed to register %s: %d\n", name, ret);
+ platform_device_put(codec_device);
+ }
+ return ret;
+}
+
+static int __init dai_init(void)
+{
+ if (msm_dai_dev_register("msm-dai")) {
+ pr_notice("dai_init: msm-dai Failed");
+ return -ENODEV;
+ }
+ return platform_driver_register(&dai_driver);
+}
+
+static void __exit dai_exit(void)
+{
+ platform_driver_unregister(&dai_driver);
+ platform_device_put(codec_device);
+}
+
+module_init(dai_init);
+module_exit(dai_exit);
+
+MODULE_DESCRIPTION("MSM I2S driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm8660-i2s.c b/sound/soc/msm/msm8660-i2s.c
new file mode 100644
index 0000000..9583c52
--- /dev/null
+++ b/sound/soc/msm/msm8660-i2s.c
@@ -0,0 +1,139 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dai.h>
+
+static int msm_cpu_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ uint32_t dma_ch = dai->id;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ ret = dai_open(dma_ch);
+ return ret;
+
+}
+
+static void msm_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ uint32_t dma_ch = dai->id;
+
+ pr_debug("%s\n", __func__);
+ dai_close(dma_ch);
+}
+
+static int msm_cpu_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s\n", __func__);
+ return 0;
+}
+
+static int msm_cpu_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ uint32_t dma_ch = dai->id;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ dai_set_master_mode(dma_ch, 1); /* CPU is master */
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ dai_set_master_mode(dma_ch, 0); /* CPU is slave */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops msm_cpu_dai_ops = {
+ .startup = msm_cpu_dai_startup,
+ .shutdown = msm_cpu_dai_shutdown,
+ .trigger = msm_cpu_dai_trigger,
+ .set_fmt = msm_cpu_dai_fmt,
+
+};
+
+
+#define MSM_DAI_SPEAKER_BUILDER(link_id) \
+{ \
+ .name = "msm-speaker-dai-"#link_id, \
+ .id = (link_id), \
+ .playback = { \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ .channels_min = 1, \
+ .channels_max = 2, \
+ .rate_max = 96000, \
+ .rate_min = 8000, \
+ }, \
+ .ops = &msm_cpu_dai_ops, \
+}
+
+
+#define MSM_DAI_MIC_BUILDER(link_id) \
+{ \
+ .name = "msm-mic-dai-"#link_id, \
+ .id = (link_id), \
+ .capture = { \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ .rate_min = 8000, \
+ .rate_max = 96000, \
+ .channels_min = 1, \
+ .channels_max = 2, \
+ }, \
+ .ops = &msm_cpu_dai_ops, \
+}
+
+
+struct snd_soc_dai msm_cpu_dai[] = {
+ MSM_DAI_SPEAKER_BUILDER(0),
+ MSM_DAI_SPEAKER_BUILDER(1),
+ MSM_DAI_SPEAKER_BUILDER(2),
+ MSM_DAI_SPEAKER_BUILDER(3),
+ MSM_DAI_SPEAKER_BUILDER(4),
+ MSM_DAI_MIC_BUILDER(5),
+ MSM_DAI_MIC_BUILDER(6),
+ MSM_DAI_MIC_BUILDER(7),
+};
+EXPORT_SYMBOL_GPL(msm_cpu_dai);
+
+static int __init msm_cpu_dai_init(void)
+{
+ return snd_soc_register_dais(msm_cpu_dai, ARRAY_SIZE(msm_cpu_dai));
+}
+module_init(msm_cpu_dai_init);
+
+static void __exit msm_cpu_dai_exit(void)
+{
+ snd_soc_unregister_dais(msm_cpu_dai, ARRAY_SIZE(msm_cpu_dai));
+}
+module_exit(msm_cpu_dai_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM CPU DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm8660-pcm.c b/sound/soc/msm/msm8660-pcm.c
new file mode 100644
index 0000000..6f6fe43
--- /dev/null
+++ b/sound/soc/msm/msm8660-pcm.c
@@ -0,0 +1,369 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/audio_dma_msm8k.h>
+#include <sound/dai.h>
+#include "msm8660-pcm.h"
+
+static const struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = DMASZ/4,
+ .buffer_bytes_max = DMASZ,
+ .rate_max = 96000,
+ .rate_min = 8000,
+ .channels_min = USE_CHANNELS_MIN,
+ .channels_max = USE_CHANNELS_MAX,
+ .periods_min = 4,
+ .periods_max = 512,
+ .fifo_size = 0,
+};
+
+struct msm_pcm_data {
+ spinlock_t lock;
+ int ch;
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+
+ pr_debug("%s\n", __func__);
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static irqreturn_t msm_pcm_irq(int intrsrc, void *data)
+{
+ struct snd_pcm_substream *substream = data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
+ int dma_ch = 0;
+ unsigned int has_xrun, pending;
+ int ret = IRQ_NONE;
+
+ if (prtd)
+ dma_ch = prtd->dma_ch;
+ else
+ return ret;
+
+ pr_debug("msm8660-pcm: msm_pcm_irq called\n");
+ pending = (intrsrc
+ & (UNDER_CH(dma_ch) | PER_CH(dma_ch) | ERR_CH(dma_ch)));
+ has_xrun = (pending & UNDER_CH(dma_ch));
+
+ if (unlikely(has_xrun) &&
+ substream->runtime &&
+ snd_pcm_running(substream)) {
+ pr_err("xrun\n");
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ ret = IRQ_HANDLED;
+ pending &= ~UNDER_CH(dma_ch);
+ }
+
+
+ if (pending & PER_CH(dma_ch)) {
+ ret = IRQ_HANDLED;
+ if (likely(substream->runtime &&
+ snd_pcm_running(substream))) {
+ /* end of buffer missed? loop back */
+ if (++prtd->period_index >= runtime->periods)
+ prtd->period_index = 0;
+ snd_pcm_period_elapsed(substream);
+ pr_debug("period elapsed\n");
+ }
+ pending &= ~PER_CH(dma_ch);
+ }
+
+ if (unlikely(pending
+ & (UNDER_CH(dma_ch) & PER_CH(dma_ch) & ERR_CH(dma_ch)))) {
+ if (pending & UNDER_CH(dma_ch))
+ pr_err("msm8660-pcm: DMA %x Underflow\n",
+ dma_ch);
+ if (pending & ERR_CH(dma_ch))
+ pr_err("msm8660-pcm: DMA %x Master Error\n",
+ dma_ch);
+
+ }
+ return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
+ struct dai_dma_params dma_params;
+ int dma_ch = 0;
+
+ if (prtd)
+ dma_ch = prtd->dma_ch;
+ else
+ return 0;
+
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ pr_debug("%s:prtd->pcm_size = %d\n", __func__, prtd->pcm_size);
+ pr_debug("%s:prtd->pcm_count = %d\n", __func__, prtd->pcm_count);
+
+ if (prtd->enabled)
+ return 0;
+
+ dma_params.src_start = runtime->dma_addr;
+ dma_params.buffer = (u8 *)runtime->dma_area;
+ dma_params.buffer_size = prtd->pcm_size;
+ dma_params.period_size = prtd->pcm_count;
+ dma_params.channels = runtime->channels;
+
+ dai_set_params(dma_ch, &dma_params);
+ register_dma_irq_handler(dma_ch, msm_pcm_irq, (void *)substream);
+
+ prtd->enabled = 1;
+ return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dai_start(prtd->dma_ch);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dai_stop(prtd->dma_ch);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
+ snd_pcm_uframes_t offset = 0;
+
+ pr_debug("%s: period_index =%d\n", __func__, prtd->period_index);
+ offset = prtd->period_index * runtime->period_size;
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+ return offset;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct msm_audio *prtd = NULL;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ snd_soc_set_runtime_hwparams(substream, &msm_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+
+ if (ret < 0) {
+ pr_err("Error setting hw_constraint\n");
+ goto err;
+ }
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_err("Error snd_pcm_hw_constraint_list failed\n");
+
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+
+ if (prtd == NULL) {
+ pr_err("Error allocating prtd\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ prtd->dma_ch = cpu_dai->id;
+ prtd->enabled = 0;
+ runtime->dma_bytes = msm_pcm_hardware.buffer_bytes_max;
+ runtime->private_data = prtd;
+err:
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
+ int dma_ch = 0;
+
+ if (prtd)
+ dma_ch = prtd->dma_ch;
+ else
+ return 0;
+
+ pr_debug("%s\n", __func__);
+ unregister_dma_irq_handler(dma_ch);
+ kfree(runtime->private_data);
+ return 0;
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vms)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ pr_debug("%s\n", __func__);
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ pr_debug("%s: snd_msm_audio_hw_params runtime->dma_addr 0x(%x)\n",
+ __func__, (unsigned int)runtime->dma_addr);
+ pr_debug("%s: snd_msm_audio_hw_params runtime->dma_area 0x(%x)\n",
+ __func__, (unsigned int)runtime->dma_area);
+ pr_debug("%s: snd_msm_audio_hw_params runtime->dma_bytes 0x(%x)\n",
+ __func__, (unsigned int)runtime->dma_bytes);
+
+ return dma_mmap_coherent(substream->pcm->card->dev, vms,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = msm_pcm_hw_params,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int pcm_preallocate_buffer(struct snd_pcm *pcm,
+ int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = msm_pcm_hardware.buffer_bytes_max;
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_coherent(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+ return 0;
+}
+
+static void msm_pcm_free_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!stream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_coherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+static u64 msm_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int msm_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &msm_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ if (dai->playback.channels_min) {
+ ret = pcm_preallocate_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ return ret;
+ }
+ if (dai->capture.channels_min) {
+ ret = pcm_preallocate_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+struct snd_soc_platform msm8660_soc_platform = {
+ .name = "msm8660-pcm-audio",
+ .pcm_ops = &msm_pcm_ops,
+ .pcm_new = msm_pcm_new,
+ .pcm_free = msm_pcm_free_buffers,
+};
+EXPORT_SYMBOL_GPL(msm8660_soc_platform);
+
+static int __init msm_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&msm8660_soc_platform);
+}
+static void __exit msm_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&msm8660_soc_platform);
+}
+module_init(msm_soc_platform_init);
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("MSM PCM module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm8660-pcm.h b/sound/soc/msm/msm8660-pcm.h
new file mode 100644
index 0000000..3bec9a7
--- /dev/null
+++ b/sound/soc/msm/msm8660-pcm.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef _MSM_PCM_H
+#define _MSM_PCM_H
+
+#define USE_CHANNELS_MIN 1
+#define USE_CHANNELS_MAX 2
+#define NUM_DMAS 9
+#define DMASZ 16384
+#define MAX_CHANNELS 9
+
+#define MSM_LPA_PHYS 0x28100000
+#define MSM_LPA_END 0x2810DFFF
+
+
+struct msm_audio {
+ struct snd_pcm_substream *substream;
+
+ /* data allocated for various buffers */
+ char *data;
+ dma_addr_t phys;
+
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ int enabled;
+ int period;
+ int dma_ch;
+ int period_index;
+ int start;
+};
+
+extern struct snd_soc_dai msm_cpu_dai[NUM_DMAS];
+extern struct snd_soc_platform msm8660_soc_platform;
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/msm8660.c b/sound/soc/msm/msm8660.c
new file mode 100644
index 0000000..8469507
--- /dev/null
+++ b/sound/soc/msm/msm8660.c
@@ -0,0 +1,342 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/mfd/pmic8901.h>
+#include <linux/platform_device.h>
+#include <mach/board.h>
+#include <mach/mpp.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/dai.h>
+#include "msm8660-pcm.h"
+#include "../codecs/timpani.h"
+
+#define PM8058_GPIO_BASE NR_MSM_GPIOS
+#define PM8901_GPIO_BASE (PM8058_GPIO_BASE + \
+ PM8058_GPIOS + PM8058_MPPS)
+#define PM8901_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio + PM8901_GPIO_BASE)
+#define GPIO_EXPANDER_GPIO_BASE \
+ (PM8901_GPIO_BASE + PM8901_MPPS)
+
+static struct clk *rx_osr_clk;
+static struct clk *rx_bit_clk;
+static struct clk *tx_osr_clk;
+static struct clk *tx_bit_clk;
+
+static int rx_hw_param_status;
+static int tx_hw_param_status;
+/* Platform specific logic */
+
+static int timpani_rx_route_enable(void)
+{
+ int ret = 0;
+ pr_debug("%s\n", __func__);
+ ret = gpio_request(109, "I2S_Clock");
+ if (ret != 0) {
+ pr_err("%s: I2s clk gpio 109 request"
+ "failed\n", __func__);
+ return ret;
+ }
+ return ret;
+}
+
+static int timpani_rx_route_disable(void)
+{
+ int ret = 0;
+ pr_debug("%s\n", __func__);
+ gpio_free(109);
+ return ret;
+}
+
+
+#define GPIO_CLASS_D1_EN (GPIO_EXPANDER_GPIO_BASE + 0)
+#define PM8901_MPP_3 (2) /* PM8901 MPP starts from 0 */
+static void config_class_d1_gpio(int enable)
+{
+ int rc;
+
+ if (enable) {
+ rc = gpio_request(GPIO_CLASS_D1_EN, "CLASSD1_EN");
+ if (rc) {
+ pr_err("%s: spkr pamp gpio %d request"
+ "failed\n", __func__, GPIO_CLASS_D1_EN);
+ return;
+ }
+ gpio_direction_output(GPIO_CLASS_D1_EN, 1);
+ gpio_set_value_cansleep(GPIO_CLASS_D1_EN, 1);
+ } else {
+ gpio_set_value_cansleep(GPIO_CLASS_D1_EN, 0);
+ gpio_free(GPIO_CLASS_D1_EN);
+ }
+}
+
+static void config_class_d0_gpio(int enable)
+{
+ int rc;
+
+ if (enable) {
+ rc = pm8901_mpp_config_digital_out(PM8901_MPP_3,
+ PM8901_MPP_DIG_LEVEL_MSMIO, 1);
+
+ if (rc) {
+ pr_err("%s: CLASS_D0_EN failed\n", __func__);
+ return;
+ }
+
+ rc = gpio_request(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3),
+ "CLASSD0_EN");
+
+ if (rc) {
+ pr_err("%s: spkr pamp gpio pm8901 mpp3 request"
+ "failed\n", __func__);
+ pm8901_mpp_config_digital_out(PM8901_MPP_3,
+ PM8901_MPP_DIG_LEVEL_MSMIO, 0);
+ return;
+ }
+
+ gpio_direction_output(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3), 1);
+ gpio_set_value_cansleep(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3), 1);
+
+ } else {
+ pm8901_mpp_config_digital_out(PM8901_MPP_3,
+ PM8901_MPP_DIG_LEVEL_MSMIO, 0);
+ gpio_set_value_cansleep(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3), 0);
+ gpio_free(PM8901_GPIO_PM_TO_SYS(PM8901_MPP_3));
+ }
+}
+
+static void timpani_poweramp_on(void)
+{
+
+ pr_debug("%s: enable stereo spkr amp\n", __func__);
+ timpani_rx_route_enable();
+ config_class_d0_gpio(1);
+ config_class_d1_gpio(1);
+}
+
+static void timpani_poweramp_off(void)
+{
+
+ pr_debug("%s: disable stereo spkr amp\n", __func__);
+ timpani_rx_route_disable();
+ config_class_d0_gpio(0);
+ config_class_d1_gpio(0);
+}
+
+static int msm8660_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int rate = params_rate(params);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (rx_hw_param_status)
+ return 0;
+ clk_set_rate(rx_osr_clk, rate * 256);
+ rx_hw_param_status++;
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (tx_hw_param_status)
+ return 0;
+ clk_set_rate(tx_osr_clk, rate * 256);
+ tx_hw_param_status++;
+ }
+ return 0;
+}
+
+static int msm8660_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rx_osr_clk = clk_get(NULL, "i2s_spkr_osr_clk");
+ if (IS_ERR(rx_osr_clk)) {
+ pr_debug("Failed to get i2s_spkr_osr_clk\n");
+ return PTR_ERR(rx_osr_clk);
+ }
+ /* Master clock OSR 256 */
+ /* Initially set to Lowest sample rate Needed */
+ clk_set_rate(rx_osr_clk, 8000 * 256);
+ ret = clk_enable(rx_osr_clk);
+ if (ret != 0) {
+ pr_debug("Unable to enable i2s_spkr_osr_clk\n");
+ clk_put(rx_osr_clk);
+ return ret;
+ }
+ rx_bit_clk = clk_get(NULL, "i2s_spkr_bit_clk");
+ if (IS_ERR(rx_bit_clk)) {
+ pr_debug("Failed to get i2s_spkr_bit_clk\n");
+ clk_disable(rx_osr_clk);
+ clk_put(rx_osr_clk);
+ return PTR_ERR(rx_bit_clk);
+ }
+ clk_set_rate(rx_bit_clk, 8);
+ ret = clk_enable(rx_bit_clk);
+ if (ret != 0) {
+ pr_debug("Unable to enable i2s_spkr_bit_clk\n");
+ clk_put(rx_bit_clk);
+ clk_disable(rx_osr_clk);
+ clk_put(rx_osr_clk);
+ return ret;
+ }
+ timpani_poweramp_on();
+ msleep(30);
+ /* End of platform specific logic */
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ tx_osr_clk = clk_get(NULL, "i2s_mic_osr_clk");
+ if (IS_ERR(tx_osr_clk)) {
+ pr_debug("Failed to get i2s_mic_osr_clk\n");
+ return PTR_ERR(tx_osr_clk);
+ }
+ /* Master clock OSR 256 */
+ clk_set_rate(tx_osr_clk, 8000 * 256);
+ ret = clk_enable(tx_osr_clk);
+ if (ret != 0) {
+ pr_debug("Unable to enable i2s_mic_osr_clk\n");
+ clk_put(tx_osr_clk);
+ return ret;
+ }
+ tx_bit_clk = clk_get(NULL, "i2s_mic_bit_clk");
+ if (IS_ERR(tx_bit_clk)) {
+ pr_debug("Failed to get i2s_mic_bit_clk\n");
+ clk_disable(tx_osr_clk);
+ clk_put(tx_osr_clk);
+ return PTR_ERR(tx_bit_clk);
+ }
+ clk_set_rate(tx_bit_clk, 8);
+ ret = clk_enable(tx_bit_clk);
+ if (ret != 0) {
+ pr_debug("Unable to enable i2s_mic_bit_clk\n");
+ clk_put(tx_bit_clk);
+ clk_disable(tx_osr_clk);
+ clk_put(tx_osr_clk);
+ return ret;
+ }
+ msm_snddev_enable_dmic_power();
+ msleep(30);
+ }
+ return ret;
+}
+
+/*
+ * TODO: rx/tx_hw_param_status should be a counter in the below code
+ * when driver starts supporting mutisession else setting it to 0
+ * will stop audio in all sessions.
+ */
+static void msm8660_shutdown(struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rx_hw_param_status = 0;
+ timpani_poweramp_off();
+ msleep(30);
+ if (rx_bit_clk) {
+ clk_disable(rx_bit_clk);
+ clk_put(rx_bit_clk);
+ rx_bit_clk = NULL;
+ }
+ if (rx_osr_clk) {
+ clk_disable(rx_osr_clk);
+ clk_put(rx_osr_clk);
+ rx_osr_clk = NULL;
+ }
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ tx_hw_param_status = 0;
+ msm_snddev_disable_dmic_power();
+ msleep(30);
+ if (tx_bit_clk) {
+ clk_disable(tx_bit_clk);
+ clk_put(tx_bit_clk);
+ tx_bit_clk = NULL;
+ }
+ if (tx_osr_clk) {
+ clk_disable(tx_osr_clk);
+ clk_put(tx_osr_clk);
+ tx_osr_clk = NULL;
+ }
+ }
+}
+
+static struct snd_soc_ops machine_ops = {
+ .startup = msm8660_startup,
+ .shutdown = msm8660_shutdown,
+ .hw_params = msm8660_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm8660_dai[] = {
+ {
+ .name = "Audio Rx",
+ .stream_name = "Audio Rx",
+ .cpu_dai = &msm_cpu_dai[0],
+ .codec_dai = &timpani_codec_dai[0],
+ .ops = &machine_ops,
+ },
+ {
+ .name = "Audio Tx",
+ .stream_name = "Audio Tx",
+ .cpu_dai = &msm_cpu_dai[5],
+ .codec_dai = &timpani_codec_dai[1],
+ .ops = &machine_ops,
+ }
+};
+
+struct snd_soc_card snd_soc_card_msm8660 = {
+ .name = "msm8660-pcm-audio",
+ .dai_link = msm8660_dai,
+ .num_links = ARRAY_SIZE(msm8660_dai),
+ .platform = &msm8660_soc_platform,
+};
+
+/* msm_audio audio subsystem */
+static struct snd_soc_device msm_snd_devdata = {
+ .card = &snd_soc_card_msm8660,
+ .codec_dev = &soc_codec_dev_timpani,
+};
+
+static struct platform_device *msm_snd_device;
+
+
+static int __init msm_audio_init(void)
+{
+ int ret;
+
+ msm_snd_device = platform_device_alloc("soc-audio", 0);
+ if (!msm_snd_device) {
+ pr_err("Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(msm_snd_device, &msm_snd_devdata);
+
+ msm_snd_devdata.dev = &msm_snd_device->dev;
+ ret = platform_device_add(msm_snd_device);
+ if (ret) {
+ platform_device_put(msm_snd_device);
+ return ret;
+ }
+
+ return ret;
+}
+module_init(msm_audio_init);
+
+static void __exit msm_audio_exit(void)
+{
+ platform_device_unregister(msm_snd_device);
+}
+module_exit(msm_audio_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MSM8660");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
new file mode 100644
index 0000000..8c0df5c
--- /dev/null
+++ b/sound/soc/msm/msm8960.c
@@ -0,0 +1,673 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dsp.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include "msm-pcm-routing.h"
+#include <../codecs/wcd9310.h>
+
+/* 8960 machine driver */
+
+#define PM8921_GPIO_BASE NR_GPIO_IRQS
+#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
+
+#define MSM_CDC_PAMPL (PM8921_GPIO_PM_TO_SYS(18))
+#define MSM_CDC_PAMPR (PM8921_GPIO_PM_TO_SYS(19))
+#define MSM8960_SPK_ON 1
+#define MSM8960_SPK_OFF 0
+
+#define msm8960_SLIM_0_RX_MAX_CHANNELS 2
+#define msm8960_SLIM_0_TX_MAX_CHANNELS 4
+
+
+static int msm8960_spk_control;
+static int msm8960_pamp_on;
+static int msm8960_slim_0_rx_ch = 1;
+static int msm8960_slim_0_tx_ch = 1;
+
+struct tabla_mbhc_calibration tabla_cal = {
+ .bias = TABLA_MICBIAS2,
+ .tldoh = 100,
+ .bg_fast_settle = 100,
+ .mic_current = TABLA_PID_MIC_5_UA,
+ .mic_pid = 100,
+ .hph_current = TABLA_PID_MIC_5_UA,
+ .setup_plug_removal_delay = 1000000,
+ .shutdown_plug_removal = 100000,
+};
+
+static struct clk *codec_clk;
+static int clk_users;
+
+static int msm8960_headset_gpios_configured;
+
+static struct snd_soc_jack hs_jack;
+
+static void codec_poweramp_on(void)
+{
+ int ret = 0;
+
+ struct pm_gpio param = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = PM_GPIO_OUT_BUF_CMOS,
+ .output_value = 1,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = PM_GPIO_VIN_S4,
+ .out_strength = PM_GPIO_STRENGTH_MED,
+ .function = PM_GPIO_FUNC_NORMAL,
+ };
+
+ if (msm8960_pamp_on)
+ return;
+
+ pr_debug("%s: enable stereo spkr amp\n", __func__);
+ ret = gpio_request(MSM_CDC_PAMPL, "CDC PAMP1");
+ if (ret) {
+ pr_err("%s: Error requesting GPIO %d\n", __func__,
+ MSM_CDC_PAMPL);
+ return;
+ }
+ ret = pm8xxx_gpio_config(MSM_CDC_PAMPL, ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure gpio %d\n", __func__,
+ MSM_CDC_PAMPL);
+ else
+ gpio_direction_output(MSM_CDC_PAMPL, 1);
+
+ ret = gpio_request(MSM_CDC_PAMPR, "CDC PAMPL");
+ if (ret) {
+ pr_err("%s: Error requesting GPIO %d\n", __func__,
+ MSM_CDC_PAMPR);
+ gpio_free(MSM_CDC_PAMPL);
+ return;
+ }
+ ret = pm8xxx_gpio_config(MSM_CDC_PAMPR, ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure gpio %d\n", __func__,
+ MSM_CDC_PAMPR);
+ else
+ gpio_direction_output(MSM_CDC_PAMPR, 1);
+
+ msm8960_pamp_on = 1;
+}
+static void codec_poweramp_off(void)
+{
+ if (!msm8960_pamp_on)
+ return;
+
+ pr_debug("%s: disable stereo spkr amp\n", __func__);
+ gpio_direction_output(MSM_CDC_PAMPL, 0);
+ gpio_free(MSM_CDC_PAMPL);
+ gpio_direction_output(MSM_CDC_PAMPR, 0);
+ gpio_free(MSM_CDC_PAMPR);
+ msm8960_pamp_on = 0;
+}
+static void msm8960_ext_control(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
+ if (msm8960_spk_control == MSM8960_SPK_ON)
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk");
+
+ snd_soc_dapm_sync(dapm);
+}
+
+static int msm8960_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
+ ucontrol->value.integer.value[0] = msm8960_spk_control;
+ return 0;
+}
+static int msm8960_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ pr_debug("%s()\n", __func__);
+ if (msm8960_spk_control == ucontrol->value.integer.value[0])
+ return 0;
+
+ msm8960_spk_control = ucontrol->value.integer.value[0];
+ msm8960_ext_control(codec);
+ return 1;
+}
+static int msm8960_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ codec_poweramp_on();
+ else
+ codec_poweramp_off();
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget msm8960_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", msm8960_spkramp_event),
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Speaker path */
+ {"Ext Spk", NULL, "LINEOUT"},
+
+ /* Microphone path */
+ {"AMIC1", NULL, "MIC BIAS1 Internal"},
+ {"DMIC1 IN", NULL, "MIC BIAS1 External"},
+ {"AMIC2", NULL, "MIC BIAS2 External"},
+ {"MIC BIAS1 Internal", NULL, "Handset Mic"},
+ {"MIC BIAS1 External", NULL, "Digital Mic1"},
+ {"MIC BIAS2 External", NULL, "Headset Mic"},
+};
+
+static const char *spk_function[] = {"Off", "On"};
+static const struct soc_enum msm8960_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static int msm8960_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm8960_slim_0_rx_ch = %d", __func__,
+ msm8960_slim_0_rx_ch);
+ ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch;
+ return 0;
+}
+
+static int msm8960_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
+ msm8960_slim_0_rx_ch);
+ return 1;
+}
+
+static int msm8960_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm8960_slim_0_tx_ch = %d", __func__,
+ msm8960_slim_0_tx_ch);
+ ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch;
+ return 0;
+}
+
+static int msm8960_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
+ msm8960_slim_0_tx_ch);
+ return 1;
+}
+
+
+static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
+ SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
+ msm8960_set_spk),
+ SOC_SINGLE_EXT("SLIM_0_RX Channels", 0, 0,
+ msm8960_SLIM_0_RX_MAX_CHANNELS, 0,
+ msm8960_slim_0_rx_ch_get, msm8960_slim_0_rx_ch_put),
+ SOC_SINGLE_EXT("SLIM_0_TX Channels", 0, 0,
+ msm8960_SLIM_0_TX_MAX_CHANNELS, 0,
+ msm8960_slim_0_tx_ch_get, msm8960_slim_0_tx_ch_put),
+};
+
+static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int err;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ pr_debug("%s()\n", __func__);
+
+ err = snd_soc_add_controls(codec, tabla_msm8960_controls,
+ ARRAY_SIZE(tabla_msm8960_controls));
+ if (err < 0)
+ return err;
+
+ snd_soc_dapm_new_controls(dapm, msm8960_dapm_widgets,
+ ARRAY_SIZE(msm8960_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+
+ snd_soc_dapm_sync(dapm);
+
+ err = snd_soc_jack_new(codec, "Headset Jack",
+ SND_JACK_HEADSET, &hs_jack);
+ if (err) {
+ pr_err("failed to create new jack\n");
+ return err;
+ }
+ tabla_hs_detect(codec, &hs_jack, &tabla_cal);
+
+ return 0;
+}
+
+/*
+ * LPA Needs only RX BE DAI links.
+ * Hence define seperate BE list for lpa
+ */
+
+static const char *lpa_mm_be[] = {
+ LPASS_BE_SLIMBUS_0_RX,
+};
+
+static struct snd_soc_dsp_link lpa_fe_media = {
+ .supported_be = lpa_mm_be,
+ .num_be = ARRAY_SIZE(lpa_mm_be),
+ .fe_playback_channels = 2,
+ .fe_capture_channels = 1,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+
+static const char *mm_be[] = {
+ LPASS_BE_SLIMBUS_0_RX,
+ LPASS_BE_SLIMBUS_0_TX,
+ LPASS_BE_HDMI,
+ LPASS_BE_INT_BT_SCO_RX,
+ LPASS_BE_INT_BT_SCO_TX,
+ LPASS_BE_INT_FM_RX,
+ LPASS_BE_INT_FM_TX,
+};
+
+static struct snd_soc_dsp_link fe_media = {
+ .supported_be = mm_be,
+ .num_be = ARRAY_SIZE(mm_be),
+ .fe_playback_channels = 2,
+ .fe_capture_channels = 1,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+
+static const char *slimbus0_hl_be[] = {
+ LPASS_BE_SLIMBUS_0_RX,
+ LPASS_BE_SLIMBUS_0_TX,
+};
+
+static struct snd_soc_dsp_link slimbus0_hl_media = {
+ .supported_be = slimbus0_hl_be,
+ .num_be = ARRAY_SIZE(slimbus0_hl_be),
+ .fe_playback_channels = 2,
+ .fe_capture_channels = 2,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+
+static const char *int_fm_hl_be[] = {
+ LPASS_BE_INT_FM_RX,
+ LPASS_BE_INT_FM_TX,
+};
+
+static struct snd_soc_dsp_link int_fm_hl_media = {
+ .supported_be = int_fm_hl_be,
+ .num_be = ARRAY_SIZE(int_fm_hl_be),
+ .fe_playback_channels = 2,
+ .fe_capture_channels = 2,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+
+static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = msm8960_slim_0_rx_ch;
+
+ return 0;
+}
+
+static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = msm8960_slim_0_tx_ch;
+
+ return 0;
+}
+
+static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+
+ return 0;
+}
+
+static int msm8960_startup(struct snd_pcm_substream *substream)
+{
+ if (clk_users++)
+ return 0;
+
+ codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
+ if (codec_clk) {
+ clk_set_rate(codec_clk, 12288000);
+ clk_enable(codec_clk);
+ } else {
+ pr_err("%s: Error setting Tabla MCLK\n", __func__);
+ clk_users--;
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void msm8960_shutdown(struct snd_pcm_substream *substream)
+{
+ clk_users--;
+ if (!clk_users) {
+ clk_disable(codec_clk);
+ clk_put(codec_clk);
+ }
+}
+
+static struct snd_soc_ops msm8960_be_ops = {
+ .startup = msm8960_startup,
+ .shutdown = msm8960_shutdown,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm8960_dai[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = "MSM8960 Media1",
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp",
+ .dynamic = 1,
+ .dsp_link = &fe_media,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {
+ .name = "MSM8960 Media2",
+ .stream_name = "MultiMedia2",
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "msm-pcm-dsp",
+ .dynamic = 1,
+ .dsp_link = &fe_media,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+ },
+ {
+ .name = "Circuit-Switch Voice",
+ .stream_name = "CS-Voice",
+ .cpu_dai_name = "CS-VOICE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dsp_link = &fe_media,
+ .be_id = MSM_FRONTEND_DAI_CS_VOICE,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ },
+ {
+ .name = "MSM VoIP",
+ .stream_name = "VoIP",
+ .cpu_dai_name = "VoIP",
+ .platform_name = "msm-voip-dsp",
+ .dynamic = 1,
+ .dsp_link = &fe_media,
+ .be_id = MSM_FRONTEND_DAI_VOIP,
+ },
+ {
+ .name = "MSM8960 LPA",
+ .stream_name = "LPA",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-lpa",
+ .dynamic = 1,
+ .dsp_link = &lpa_fe_media,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+ /* Hostless PMC purpose */
+ {
+ .name = "SLIMBUS_0 Hostless",
+ .stream_name = "SLIMBUS_0 Hostless",
+ .cpu_dai_name = "SLIMBUS0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dsp_link = &slimbus0_hl_media,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* .be_id = do not care */
+ },
+ {
+ .name = "INT_FM Hostless",
+ .stream_name = "INT_FM Hostless",
+ .cpu_dai_name = "INT_FM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dsp_link = &int_fm_hl_media,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* .be_id = do not care */
+ },
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_SLIMBUS_0_RX,
+ .stream_name = "Slimbus Playback",
+ .cpu_dai_name = "msm-dai-q6.16384",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tabla_codec",
+ .codec_dai_name = "tabla_rx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ .init = &msm8960_audrx_init,
+ .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
+ .ops = &msm8960_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_0_TX,
+ .stream_name = "Slimbus Capture",
+ .cpu_dai_name = "msm-dai-q6.16385",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tabla_codec",
+ .codec_dai_name = "tabla_tx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
+ .ops = &msm8960_be_ops,
+ },
+ /* Backend BT/FM DAI Links */
+ {
+ .name = LPASS_BE_INT_BT_SCO_RX,
+ .stream_name = "Internal BT-SCO Playback",
+ .cpu_dai_name = "msm-dai-q6.12288",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ },
+ {
+ .name = LPASS_BE_INT_BT_SCO_TX,
+ .stream_name = "Internal BT-SCO Capture",
+ .cpu_dai_name = "msm-dai-q6.12289",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ },
+ {
+ .name = LPASS_BE_INT_FM_RX,
+ .stream_name = "Internal FM Playback",
+ .cpu_dai_name = "msm-dai-q6.12292",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_RX,
+ .be_hw_params_fixup = msm8960_be_hw_params_fixup,
+ },
+ {
+ .name = LPASS_BE_INT_FM_TX,
+ .stream_name = "Internal FM Capture",
+ .cpu_dai_name = "msm-dai-q6.12293",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_TX,
+ .be_hw_params_fixup = msm8960_be_hw_params_fixup,
+ },
+ /* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "msm-dai-q6.8",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .no_codec = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = msm8960_be_hw_params_fixup,
+ },
+};
+
+struct snd_soc_card snd_soc_card_msm8960 = {
+ .name = "msm8960-snd-card",
+ .dai_link = msm8960_dai,
+ .num_links = ARRAY_SIZE(msm8960_dai),
+};
+
+static struct platform_device *msm8960_snd_device;
+
+static int msm8960_configure_headset_mic_gpios(void)
+{
+ int ret;
+ struct pm_gpio param = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = PM_GPIO_OUT_BUF_CMOS,
+ .output_value = 1,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = PM_GPIO_VIN_S4,
+ .out_strength = PM_GPIO_STRENGTH_MED,
+ .function = PM_GPIO_FUNC_NORMAL,
+ };
+
+ ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
+ if (ret) {
+ pr_err("%s: Failed to request gpio %d\n", __func__,
+ PM8921_GPIO_PM_TO_SYS(23));
+ return ret;
+ }
+
+ ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure gpio %d\n", __func__,
+ PM8921_GPIO_PM_TO_SYS(23));
+ else
+ gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
+
+ ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
+ if (ret) {
+ pr_err("%s: Failed to request gpio %d\n", __func__,
+ PM8921_GPIO_PM_TO_SYS(35));
+ gpio_free(PM8921_GPIO_PM_TO_SYS(23));
+ return ret;
+ }
+ ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure gpio %d\n", __func__,
+ PM8921_GPIO_PM_TO_SYS(35));
+ else
+ gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
+
+ return 0;
+}
+static void msm8960_free_headset_mic_gpios(void)
+{
+ if (msm8960_headset_gpios_configured) {
+ gpio_free(PM8921_GPIO_PM_TO_SYS(23));
+ gpio_free(PM8921_GPIO_PM_TO_SYS(35));
+ }
+}
+
+static int __init msm8960_audio_init(void)
+{
+ int ret;
+
+ msm8960_snd_device = platform_device_alloc("soc-audio", 0);
+ if (!msm8960_snd_device) {
+ pr_err("Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
+ ret = platform_device_add(msm8960_snd_device);
+ if (ret) {
+ platform_device_put(msm8960_snd_device);
+ return ret;
+ }
+
+ if (msm8960_configure_headset_mic_gpios()) {
+ pr_err("%s Fail to configure headset mic gpios\n", __func__);
+ msm8960_headset_gpios_configured = 0;
+ } else
+ msm8960_headset_gpios_configured = 1;
+
+ return ret;
+
+}
+module_init(msm8960_audio_init);
+
+static void __exit msm8960_audio_exit(void)
+{
+ msm8960_free_headset_mic_gpios();
+ platform_device_unregister(msm8960_snd_device);
+}
+module_exit(msm8960_audio_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MSM8960");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm8x60-dai.c b/sound/soc/msm/msm8x60-dai.c
new file mode 100644
index 0000000..8130f07
--- /dev/null
+++ b/sound/soc/msm/msm8x60-dai.c
@@ -0,0 +1,148 @@
+/* sound/soc/msm/msm-dai.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Derived from msm-pcm.c and msm7201.c.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include "msm8x60-pcm.h"
+
+static struct snd_soc_dai_driver msm_pcm_codec_dais[] = {
+{
+ .name = "msm-codec-dai",
+ .playback = {
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+static struct snd_soc_dai_driver msm_pcm_cpu_dais[] = {
+{
+ .name = "msm-cpu-dai",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_msm = {
+ .compress_type = SND_SOC_FLAT_COMPRESSION,
+};
+
+static __devinit int asoc_msm_codec_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm,
+ msm_pcm_codec_dais, ARRAY_SIZE(msm_pcm_codec_dais));
+}
+
+static int __devexit asoc_msm_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static __devinit int asoc_msm_cpu_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_dai(&pdev->dev, msm_pcm_cpu_dais);
+}
+
+static int __devexit asoc_msm_cpu_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver asoc_msm_codec_driver = {
+ .probe = asoc_msm_codec_probe,
+ .remove = __devexit_p(asoc_msm_codec_remove),
+ .driver = {
+ .name = "msm-codec-dai",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_driver asoc_msm_cpu_driver = {
+ .probe = asoc_msm_cpu_probe,
+ .remove = __devexit_p(asoc_msm_cpu_remove),
+ .driver = {
+ .name = "msm-cpu-dai",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_codec_dai_init(void)
+{
+ return platform_driver_register(&asoc_msm_codec_driver);
+}
+
+static void __exit msm_codec_dai_exit(void)
+{
+ platform_driver_unregister(&asoc_msm_codec_driver);
+}
+
+static int __init msm_cpu_dai_init(void)
+{
+ return platform_driver_register(&asoc_msm_cpu_driver);
+}
+
+static void __exit msm_cpu_dai_exit(void)
+{
+ platform_driver_unregister(&asoc_msm_cpu_driver);
+}
+
+module_init(msm_codec_dai_init);
+module_exit(msm_codec_dai_exit);
+module_init(msm_cpu_dai_init);
+module_exit(msm_cpu_dai_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM Codec/Cpu Dai driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm8x60-pcm.c b/sound/soc/msm/msm8x60-pcm.c
new file mode 100644
index 0000000..a6f7350
--- /dev/null
+++ b/sound/soc/msm/msm8x60-pcm.c
@@ -0,0 +1,806 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/android_pmem.h>
+#include <mach/qdsp6v2/audio_dev_ctl.h>
+
+#include "msm8x60-pcm.h"
+
+struct snd_msm {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 960 * 10,
+ .period_bytes_min = 960 * 5,
+ .period_bytes_max = 960 * 5,
+ .periods_min = 2,
+ .periods_max = 2,
+ .fifo_size = 0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+uint32_t in_frame_info[8][2];
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+ .count = ARRAY_SIZE(supported_sample_rates),
+ .list = supported_sample_rates,
+ .mask = 0,
+};
+
+static void alsa_out_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ int ret = 0;
+ struct msm_audio *prtd = (struct msm_audio *) private_data;
+ int dev_rate = 48000;
+ pr_debug("evt_id = 0x%8x\n", evt_id);
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ pr_debug("AUDDEV_EVT_DEV_RDY\n");
+ prtd->copp_id = evt_payload->routing_id;
+ pr_debug("prtd->session_id = %d, copp_id= %d",
+ prtd->session_id, prtd->copp_id);
+ if (prtd->copp_id == PCM_RX)
+ dev_rate = 8000;
+
+ ret = msm_snddev_set_dec(prtd->session_id, prtd->copp_id, 1,
+ dev_rate, 1);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ pr_debug("AUDDEV_EVT_DEV_RLS\n");
+ prtd->copp_id = evt_payload->routing_id;
+ pr_debug("prtd->session_id = %d, copp_id= %d",
+ prtd->session_id, prtd->copp_id);
+ if (prtd->copp_id == PCM_RX)
+ dev_rate = 8000;
+
+ ret = msm_snddev_set_dec(prtd->session_id, prtd->copp_id, 0,
+ dev_rate, 1);
+ break;
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ pr_debug("AUDDEV_EVT_STREAM_VOL_CHG\n");
+ break;
+ default:
+ pr_debug("Unknown Event\n");
+ break;
+ }
+}
+
+static void alsa_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ int ret = 0;
+ struct msm_audio *prtd = (struct msm_audio *) private_data;
+ int dev_rate = 48000;
+ pr_debug("evt_id = 0x%8x\n", evt_id);
+
+ switch (evt_id) {
+ case AUDDEV_EVT_DEV_RDY:
+ prtd->copp_id = evt_payload->routing_id;
+ if (prtd->copp_id == PCM_TX)
+ dev_rate = 8000;
+
+ ret = msm_snddev_set_enc(prtd->session_id, prtd->copp_id, 1,
+ dev_rate, 1);
+ break;
+ case AUDDEV_EVT_DEV_RLS:
+ prtd->copp_id = evt_payload->routing_id;
+ if (prtd->copp_id == PCM_TX)
+ dev_rate = 8000;
+
+ ret = msm_snddev_set_enc(prtd->session_id, prtd->copp_id, 0,
+ dev_rate, 1);
+ break;
+ default:
+ pr_debug("Unknown Event\n");
+ break;
+ }
+}
+
+static void event_handler(uint32_t opcode,
+ uint32_t token, uint32_t *payload, void *priv)
+{
+ struct msm_audio *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+ uint32_t *ptrmem = (uint32_t *)payload;
+ int i = 0;
+
+ pr_debug("%s\n", __func__);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE: {
+ pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
+ pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+ prtd->pcm_irq_pos += prtd->pcm_count;
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ atomic_inc(&prtd->out_count);
+ wake_up(&the_locks.write_wait);
+ if (!atomic_read(&prtd->start))
+ break;
+ if (!prtd->mmap_flag)
+ break;
+ pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+ __func__, prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+ break;
+ }
+ case ASM_DATA_CMDRSP_EOS:
+ pr_debug("ASM_DATA_CMDRSP_EOS\n");
+ prtd->cmd_ack = 1;
+ wake_up(&the_locks.eos_wait);
+ break;
+ case ASM_DATA_EVENT_READ_DONE: {
+ pr_debug("ASM_DATA_EVENT_READ_DONE\n");
+ pr_debug("token = 0x%08x\n", token);
+ for (i = 0; i < 8; i++, ++ptrmem)
+ pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+ in_frame_info[token][0] = payload[2];
+ in_frame_info[token][1] = payload[3];
+ prtd->pcm_irq_pos += in_frame_info[token][0];
+ pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
+ if (atomic_read(&prtd->start))
+ snd_pcm_period_elapsed(substream);
+ if (atomic_read(&prtd->in_count) <= prtd->periods)
+ atomic_inc(&prtd->in_count);
+ wake_up(&the_locks.read_wait);
+ if (prtd->mmap_flag)
+ q6asm_read_nolock(prtd->audio_client);
+ break;
+ }
+ case APR_BASIC_RSP_RESULT: {
+ if (!prtd->mmap_flag
+ && !atomic_read(&prtd->out_needed))
+ break;
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_RUN:
+ if (substream->stream
+ != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ if (prtd->mmap_flag) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ } else {
+ while (atomic_read(&prtd->out_needed)) {
+ pr_debug("%s:writing %d bytes"
+ " of buffer to dsp\n",
+ __func__,
+ prtd->pcm_count);
+ q6asm_write_nolock(prtd->audio_client,
+ prtd->pcm_count,
+ 0, 0, NO_TIMESTAMP);
+ atomic_dec(&prtd->out_needed);
+ wake_up(&the_locks.write_wait);
+ };
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+ break;
+ }
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret;
+ int dev_rate = 48000;
+ int i = 0;
+
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+ if (prtd->enabled)
+ return 0;
+
+ ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
+ runtime->channels);
+ if (ret < 0)
+ pr_debug("%s: CMD Format block failed\n", __func__);
+
+ atomic_set(&prtd->out_count, runtime->periods);
+ atomic_set(&prtd->in_count, 0);
+ for (i = 0; i < MAX_COPP; i++) {
+ pr_debug("prtd->session_id = %d, copp_id= %d",
+ prtd->session_id, i);
+ if (session_route.playback_session[substream->number][i]
+ != DEVICE_IGNORE) {
+ pr_err("Device active\n");
+ if (i == PCM_RX)
+ dev_rate = 8000;
+ msm_snddev_set_dec(prtd->session_id,
+ i, 1, dev_rate, runtime->channels);
+ }
+ }
+ prtd->enabled = 1;
+ prtd->cmd_ack = 0;
+ return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int ret = 0;
+ int i = 0;
+ int dev_rate = 48000;
+ pr_debug("%s\n", __func__);
+ prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+ prtd->pcm_irq_pos = 0;
+
+ /* rate and channels are sent to audio driver */
+ prtd->samp_rate = runtime->rate;
+ prtd->channel_mode = runtime->channels;
+
+ if (prtd->enabled)
+ return 0;
+
+ pr_debug("Samp_rate = %d\n", prtd->samp_rate);
+ pr_debug("Channel = %d\n", prtd->channel_mode);
+ ret = q6asm_enc_cfg_blk_pcm(prtd->audio_client, prtd->samp_rate,
+ prtd->channel_mode);
+ if (ret < 0)
+ pr_debug("%s: cmd cfg pcm was block failed", __func__);
+
+ for (i = 0; i < runtime->periods; i++)
+ q6asm_read_nolock(prtd->audio_client);
+ prtd->periods = runtime->periods;
+ for (i = 0; i < MAX_COPP; i++) {
+ pr_debug("prtd->session_id = %d, copp_id= %d",
+ prtd->session_id,
+ session_route.capture_session[prtd->session_id][i]);
+ if (session_route.capture_session[prtd->session_id][i]
+ != DEVICE_IGNORE) {
+ if (i == PCM_RX)
+ dev_rate = 8000;
+ msm_snddev_set_enc(prtd->session_id, i, 1, dev_rate, 1);
+ }
+ }
+ prtd->enabled = 1;
+
+ return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int ret = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ pr_debug("%s\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("SNDRV_PCM_TRIGGER_START\n");
+ q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ atomic_set(&prtd->start, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+ atomic_set(&prtd->start, 0);
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ break;
+ prtd->cmd_ack = 0;
+ q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+ q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+ atomic_set(&prtd->start, 0);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+ if (prtd == NULL) {
+ pr_err("Failed to allocate memory for msm_audio\n");
+ return -ENOMEM;
+ }
+ runtime->hw = msm_pcm_hardware;
+ prtd->substream = substream;
+ prtd->audio_client = q6asm_audio_client_alloc(
+ (app_cb)event_handler, prtd);
+ if (!prtd->audio_client) {
+ pr_debug("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm out open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+ /* Capture path */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM);
+ if (ret < 0) {
+ pr_err("%s: pcm in open failed\n", __func__);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ }
+ /* The session id returned by q6asm_open_read above is random and
+ * hence we cannot use the session id to route from user space.
+ * This results in need of a hardcoded session id for both playback
+ * and capture sessions. we can use the subdevice id to identify
+ * the session and use that for routing. Hence using
+ * substream->number as the session id for routing purpose. However
+ * DSP understands the session based on the allocated session id,
+ * hence using the variable prtd->session_id for all dsp commands.
+ */
+
+ prtd->session_id = prtd->audio_client->session;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ prtd->cmd_ack = 1;
+ prtd->device_events = AUDDEV_EVT_DEV_RDY |
+ AUDDEV_EVT_STREAM_VOL_CHG |
+ AUDDEV_EVT_DEV_RLS;
+ prtd->source = msm_snddev_route_dec(prtd->session_id);
+ pr_debug("Register device event listener for"
+ "SNDRV_PCM_STREAM_PLAYBACK session %d\n",
+ substream->number);
+ ret = auddev_register_evt_listner(prtd->device_events,
+ AUDDEV_CLNT_DEC, substream->number,
+ alsa_out_listener, (void *) prtd);
+ if (ret)
+ pr_debug("failed to register device event listener\n");
+ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ prtd->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+ AUDDEV_EVT_FREQ_CHG;
+ prtd->source = msm_snddev_route_enc(prtd->session_id);
+ pr_debug("Register device event listener for"
+ "SNDRV_PCM_STREAM_CAPTURE session %d\n",
+ substream->number);
+ ret = auddev_register_evt_listner(prtd->device_events,
+ AUDDEV_CLNT_ENC, substream->number,
+ alsa_in_listener, (void *) prtd);
+ if (ret)
+ pr_debug("failed to register device event listener\n");
+ }
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_sample_rates);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_list failed\n");
+ /* Ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ pr_debug("snd_pcm_hw_constraint_integer failed\n");
+
+ prtd->dsp_cnt = 0;
+ runtime->private_data = prtd;
+
+ return 0;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer = 0;
+ char *bufptr = NULL;
+ void *data = NULL;
+ uint32_t idx = 0;
+ uint32_t size = 0;
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ fbytes = frames_to_bytes(runtime, frames);
+ pr_debug("%s: prtd->out_count = %d\n",
+ __func__, atomic_read(&prtd->out_count));
+ ret = wait_event_timeout(the_locks.write_wait,
+ (atomic_read(&prtd->out_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_debug("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+
+ if (!atomic_read(&prtd->out_count)) {
+ pr_debug("%s: pcm stopped out_count 0\n", __func__);
+ return 0;
+ }
+ data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ if (bufptr) {
+ pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
+ __func__, fbytes, xfer, size);
+ xfer = fbytes;
+ if (copy_from_user(bufptr, buf, xfer)) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ buf += xfer;
+ fbytes -= xfer;
+ pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes, xfer);
+ if (atomic_read(&prtd->start)) {
+ pr_debug("%s:writing %d bytes of buffer to dsp\n",
+ __func__, xfer);
+ ret = q6asm_write_nolock(prtd->audio_client, xfer,
+ 0, 0, NO_TIMESTAMP);
+ if (ret < 0) {
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ atomic_inc(&prtd->out_needed);
+ atomic_dec(&prtd->out_count);
+ }
+fail:
+ return ret;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = 0;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ dir = IN;
+ ret = wait_event_timeout(the_locks.eos_wait,
+ prtd->cmd_ack, 5 * HZ);
+ if (ret < 0)
+ pr_err("%s: CMD_EOS failed\n", __func__);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+
+ pr_debug("%s\n", __func__);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC,
+ substream->number);
+ pr_debug("%s\n", __func__);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ msm_clear_session_id(prtd->session_id);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+ snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+ int fbytes = 0;
+ int xfer;
+ char *bufptr;
+ void *data = NULL;
+ static uint32_t idx;
+ static uint32_t size;
+ uint32_t offset = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = substream->runtime->private_data;
+
+
+ pr_debug("%s\n", __func__);
+ fbytes = frames_to_bytes(runtime, frames);
+
+ pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
+ pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
+ pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
+
+ ret = wait_event_timeout(the_locks.read_wait,
+ (atomic_read(&prtd->in_count)), 5 * HZ);
+ if (ret < 0) {
+ pr_debug("%s: wait_event_timeout failed\n", __func__);
+ goto fail;
+ }
+ if (!atomic_read(&prtd->in_count)) {
+ pr_debug("%s: pcm stopped in_count 0\n", __func__);
+ return 0;
+ }
+ pr_debug("Checking if valid buffer is available...%08x\n",
+ (unsigned int) data);
+ data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
+ bufptr = data;
+ pr_debug("Size = %d\n", size);
+ pr_debug("fbytes = %d\n", fbytes);
+ pr_debug("idx = %d\n", idx);
+ if (bufptr) {
+ xfer = fbytes;
+ if (xfer > size)
+ xfer = size;
+ offset = in_frame_info[idx][1];
+ pr_debug("Offset value = %d\n", offset);
+ if (copy_to_user(buf, bufptr+offset, xfer)) {
+ pr_err("Failed to copy buf to user\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ fbytes -= xfer;
+ size -= xfer;
+ in_frame_info[idx][1] += xfer;
+ pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
+ __func__, fbytes, size, xfer);
+ pr_debug(" Sending next buffer to dsp\n");
+ memset(&in_frame_info[idx], 0,
+ sizeof(uint32_t) * 2);
+ atomic_dec(&prtd->in_count);
+ ret = q6asm_read_nolock(prtd->audio_client);
+ if (ret < 0) {
+ pr_err("q6asm read failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+ } else
+ pr_err("No valid buffer\n");
+
+ pr_debug("Returning from capture_copy... %d\n", ret);
+fail:
+ return ret;
+}
+
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ int dir = OUT;
+
+ pr_debug("%s\n", __func__);
+ q6asm_audio_client_buf_free_contiguous(dir,
+ prtd->audio_client);
+ auddev_unregister_evt_listner(AUDDEV_CLNT_ENC,
+ substream->number);
+ q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+ msm_clear_session_id(prtd->session_id);
+ q6asm_audio_client_free(prtd->audio_client);
+ kfree(prtd);
+
+ return 0;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+ snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+ return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_close(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_close(substream);
+ return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = msm_pcm_playback_prepare(substream);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ ret = msm_pcm_capture_prepare(substream);
+ return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ if (prtd->pcm_irq_pos >= prtd->pcm_size)
+ prtd->pcm_irq_pos = 0;
+ pr_debug("%s: pcm_irq_pos = %d\n", __func__, prtd->pcm_irq_pos);
+ return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+int msm_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+ prtd->mmap_flag = 1;
+ dma_mmap_coherent(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return 0;
+}
+
+int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msm_audio *prtd = runtime->private_data;
+ struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+ struct audio_buffer *buf;
+ int dir, ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = IN;
+ else
+ dir = OUT;
+
+ ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+ prtd->audio_client,
+ runtime->hw.period_bytes_min,
+ runtime->hw.periods_max);
+ if (ret < 0) {
+ pr_err("Audio Start: Buffer Allocation failed \
+ rc = %d\n", ret);
+ return -ENOMEM;
+ }
+ buf = prtd->audio_client->port[dir].buf;
+
+ pr_debug("%s:buf = %p\n", __func__, buf);
+ dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ dma_buf->dev.dev = substream->pcm->card->dev;
+ dma_buf->private_data = NULL;
+ dma_buf->area = buf[0].data;
+ dma_buf->addr = buf[0].phys;
+ dma_buf->bytes = runtime->hw.buffer_bytes_max;
+ if (!dma_buf->area)
+ return -ENOMEM;
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+static struct snd_pcm_ops msm_pcm_ops = {
+ .open = msm_pcm_open,
+ .copy = msm_pcm_copy,
+ .hw_params = msm_pcm_hw_params,
+ .close = msm_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .prepare = msm_pcm_prepare,
+ .trigger = msm_pcm_trigger,
+ .pointer = msm_pcm_pointer,
+ .mmap = msm_pcm_mmap,
+};
+
+static int msm_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+
+ ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 2);
+ if (ret)
+ return ret;
+ ret = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, 1);
+ if (ret)
+ return ret;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &msm_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &msm_pcm_ops);
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ return ret;
+}
+
+struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_pcm_ops,
+ .pcm_new = msm_pcm_new,
+};
+EXPORT_SYMBOL(msm_soc_platform);
+
+static __devinit int msm_pcm_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_pcm_driver = {
+ .probe = msm_pcm_probe,
+ .remove = __devexit_p(msm_pcm_remove),
+ .driver = {
+ .name = "msm-dsp-audio",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm8x60-pcm.h b/sound/soc/msm/msm8x60-pcm.h
new file mode 100644
index 0000000..d7c9ad8
--- /dev/null
+++ b/sound/soc/msm/msm8x60-pcm.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#ifndef _MSM_PCM_H
+#define _MSM_PCM_H
+#include <sound/apr_audio.h>
+#include <sound/q6asm.h>
+
+#define MAX_PLAYBACK_SESSIONS 2
+#define MAX_CAPTURE_SESSIONS 1
+#define MAX_COPP 12
+
+extern int copy_count;
+
+struct buffer {
+ void *data;
+ unsigned size;
+ unsigned used;
+ unsigned addr;
+};
+
+struct buffer_rec {
+ void *data;
+ unsigned int size;
+ unsigned int read;
+ unsigned int addr;
+};
+
+struct audio_locks {
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t eos_wait;
+ wait_queue_head_t enable_wait;
+};
+
+extern struct audio_locks the_locks;
+
+struct msm_audio {
+ struct snd_pcm_substream *substream;
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_irq_pos; /* IRQ position */
+ uint16_t source; /* Encoding source bit mask */
+
+ struct audio_client *audio_client;
+
+ uint16_t session_id;
+ int copp_id;
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+ uint32_t dsp_cnt;
+
+ uint32_t device_events; /* device events interested in */
+ int abort; /* set when error, like sample rate mismatch */
+
+ int enabled;
+ int close_ack;
+ int cmd_ack;
+ atomic_t start;
+ atomic_t out_count;
+ atomic_t in_count;
+ atomic_t out_needed;
+ int periods;
+ int mmap_flag;
+};
+
+struct pcm_session {
+ unsigned short playback_session[MAX_PLAYBACK_SESSIONS][MAX_COPP];
+ unsigned short capture_session[MAX_CAPTURE_SESSIONS][MAX_COPP];
+};
+
+/* platform data */
+extern struct snd_soc_platform_driver msm_soc_platform;
+extern struct pcm_session session_route;
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/msm8x60.c b/sound/soc/msm/msm8x60.c
new file mode 100644
index 0000000..59597f9
--- /dev/null
+++ b/sound/soc/msm/msm8x60.c
@@ -0,0 +1,1108 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6afe.h>
+#include <asm/dma.h>
+#include <asm/mach-types.h>
+#include <mach/qdsp6v2/audio_dev_ctl.h>
+
+#define LOOPBACK_ENABLE 0x1
+#define LOOPBACK_DISABLE 0x0
+
+#include "msm8x60-pcm.h"
+
+static struct platform_device *msm_audio_snd_device;
+struct audio_locks the_locks;
+EXPORT_SYMBOL(the_locks);
+struct msm_volume msm_vol_ctl;
+EXPORT_SYMBOL(msm_vol_ctl);
+struct pcm_session session_route;
+EXPORT_SYMBOL(session_route);
+static struct snd_kcontrol_new snd_msm_controls[];
+
+char snddev_name[AUDIO_DEV_CTL_MAX_DEV][44];
+#define MSM_MAX_VOLUME 0x2000
+#define MSM_VOLUME_STEP ((MSM_MAX_VOLUME+17)/100) /* 17 added to avoid
+ more deviation */
+static int device_index; /* Count of Device controls */
+static int simple_control; /* Count of simple controls*/
+static int src_dev;
+static int dst_dev;
+static int loopback_status;
+
+static int msm_scontrol_count_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int msm_scontrol_count_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = simple_control;
+ return 0;
+}
+
+static int msm_v_call_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int msm_v_call_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_v_call_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int start = ucontrol->value.integer.value[0];
+ if (start)
+ broadcast_event(AUDDEV_EVT_START_VOICE, DEVICE_IGNORE,
+ SESSION_IGNORE);
+ else
+ broadcast_event(AUDDEV_EVT_END_VOICE, DEVICE_IGNORE,
+ SESSION_IGNORE);
+ return 0;
+}
+
+static int msm_v_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 2;
+ return 0;
+}
+
+static int msm_v_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_v_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int dir = ucontrol->value.integer.value[0];
+ int mute = ucontrol->value.integer.value[1];
+ return msm_set_voice_mute(dir, mute);
+}
+
+static int msm_v_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2; /* Volume */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ return 0;
+}
+
+static int msm_v_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_v_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int dir = ucontrol->value.integer.value[0];
+ int volume = ucontrol->value.integer.value[1];
+
+ return msm_set_voice_vol(dir, volume);
+}
+
+static int msm_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2; /* Volume */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 16383;
+ return 0;
+}
+static int msm_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ int session_id = ucontrol->value.integer.value[0];
+ int volume = ucontrol->value.integer.value[1];
+ int factor = ucontrol->value.integer.value[2];
+ u64 session_mask = 0;
+
+ if (factor > 10000)
+ return -EINVAL;
+
+ if ((volume < 0) || (volume/factor > 100))
+ return -EINVAL;
+
+ volume = (MSM_VOLUME_STEP * volume);
+
+ /* Convert back to original decimal point by removing the 10-base factor
+ * and discard the fractional portion
+ */
+
+ volume = volume/factor;
+
+ if (volume > MSM_MAX_VOLUME)
+ volume = MSM_MAX_VOLUME;
+
+ /* Only Decoder volume control supported */
+ session_mask = (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \
+ ((int)AUDDEV_CLNT_DEC-1));
+ msm_vol_ctl.volume = volume;
+ pr_debug("%s:session_id %d, volume %d", __func__, session_id, volume);
+ broadcast_event(AUDDEV_EVT_STREAM_VOL_CHG, DEVICE_IGNORE,
+ session_mask);
+
+ return ret;
+}
+
+static int msm_voice_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3; /* Device */
+
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = msm_snddev_devcount();
+ return 0;
+}
+
+static int msm_voice_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ uint32_t rx_dev_id;
+ uint32_t tx_dev_id;
+ struct msm_snddev_info *rx_dev_info;
+ struct msm_snddev_info *tx_dev_info;
+ int set = ucontrol->value.integer.value[2];
+ u64 session_mask;
+
+ if (!set)
+ return -EPERM;
+ /* Rx Device Routing */
+ rx_dev_id = ucontrol->value.integer.value[0];
+ rx_dev_info = audio_dev_ctrl_find_dev(rx_dev_id);
+
+ if (IS_ERR(rx_dev_info)) {
+ pr_err("%s:pass invalid dev_id\n", __func__);
+ rc = PTR_ERR(rx_dev_info);
+ return rc;
+ }
+
+ if (!(rx_dev_info->capability & SNDDEV_CAP_RX)) {
+ pr_err("%s:First Dev is supposed to be RX\n", __func__);
+ return -EFAULT;
+ }
+
+ pr_debug("%s:route cfg %d STREAM_VOICE_RX type\n",
+ __func__, rx_dev_id);
+
+ msm_set_voc_route(rx_dev_info, AUDIO_ROUTE_STREAM_VOICE_RX,
+ rx_dev_id);
+
+ session_mask = ((u64)0x1) << (MAX_BIT_PER_CLIENT * \
+ ((int)AUDDEV_CLNT_VOC-1));
+
+ broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, rx_dev_id, session_mask);
+
+
+ /* Tx Device Routing */
+ tx_dev_id = ucontrol->value.integer.value[1];
+ tx_dev_info = audio_dev_ctrl_find_dev(tx_dev_id);
+
+ if (IS_ERR(tx_dev_info)) {
+ pr_err("%s:pass invalid dev_id\n", __func__);
+ rc = PTR_ERR(tx_dev_info);
+ return rc;
+ }
+
+ if (!(tx_dev_info->capability & SNDDEV_CAP_TX)) {
+ pr_err("%s:Second Dev is supposed to be Tx\n", __func__);
+ return -EFAULT;
+ }
+
+ pr_debug("%s:route cfg %d %d type\n",
+ __func__, tx_dev_id, AUDIO_ROUTE_STREAM_VOICE_TX);
+
+ msm_set_voc_route(tx_dev_info, AUDIO_ROUTE_STREAM_VOICE_TX,
+ tx_dev_id);
+
+ broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, tx_dev_id, session_mask);
+
+ if (rx_dev_info->opened)
+ broadcast_event(AUDDEV_EVT_DEV_RDY, rx_dev_id, session_mask);
+
+ if (tx_dev_info->opened)
+ broadcast_event(AUDDEV_EVT_DEV_RDY, tx_dev_id, session_mask);
+
+ return rc;
+}
+
+static int msm_voice_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ /* TODO: query Device list */
+ return 0;
+}
+
+static int msm_device_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1; /* Device */
+
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = msm_snddev_devcount();
+ return 0;
+}
+
+static int msm_device_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ int set = 0;
+ struct msm_audio_route_config route_cfg;
+ struct msm_snddev_info *dev_info;
+ struct msm_snddev_info *dst_dev_info;
+ struct msm_snddev_info *src_dev_info;
+ int tx_freq = 0;
+ int rx_freq = 0;
+ u32 set_freq = 0;
+
+ set = ucontrol->value.integer.value[0];
+ route_cfg.dev_id = ucontrol->id.numid - device_index;
+ dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
+ if (IS_ERR(dev_info)) {
+ pr_err("%s:pass invalid dev_id\n", __func__);
+ rc = PTR_ERR(dev_info);
+ return rc;
+ }
+ pr_info("%s:device %s set %d\n", __func__, dev_info->name, set);
+
+ if (set) {
+ if (!dev_info->opened) {
+ set_freq = dev_info->sample_rate;
+ if (!msm_device_is_voice(route_cfg.dev_id)) {
+ msm_get_voc_freq(&tx_freq, &rx_freq);
+ if (dev_info->capability & SNDDEV_CAP_TX)
+ set_freq = tx_freq;
+
+ if (set_freq == 0)
+ set_freq = dev_info->sample_rate;
+ } else
+ set_freq = dev_info->sample_rate;
+
+
+ pr_err("%s:device freq =%d\n", __func__, set_freq);
+ rc = dev_info->dev_ops.set_freq(dev_info, set_freq);
+ if (rc < 0) {
+ pr_err("%s:device freq failed!\n", __func__);
+ return rc;
+ }
+ dev_info->set_sample_rate = rc;
+ rc = 0;
+ rc = dev_info->dev_ops.open(dev_info);
+ if (rc < 0) {
+ pr_err("%s:Enabling %s failed\n",
+ __func__, dev_info->name);
+ return rc;
+ }
+ dev_info->opened = 1;
+ broadcast_event(AUDDEV_EVT_DEV_RDY, route_cfg.dev_id,
+ SESSION_IGNORE);
+ if ((route_cfg.dev_id == src_dev) ||
+ (route_cfg.dev_id == dst_dev)) {
+ dst_dev_info = audio_dev_ctrl_find_dev(
+ dst_dev);
+ if (IS_ERR(dst_dev_info)) {
+ pr_err("dst_dev:%s:pass invalid"
+ "dev_id\n", __func__);
+ rc = PTR_ERR(dst_dev_info);
+ return rc;
+ }
+ src_dev_info = audio_dev_ctrl_find_dev(
+ src_dev);
+ if (IS_ERR(src_dev_info)) {
+ pr_err("src_dev:%s:pass invalid"
+ "dev_id\n", __func__);
+ rc = PTR_ERR(src_dev_info);
+ return rc;
+ }
+ if ((dst_dev_info->opened) &&
+ (src_dev_info->opened)) {
+ pr_debug("%d: Enable afe_loopback\n",
+ __LINE__);
+ afe_loopback(LOOPBACK_ENABLE,
+ dst_dev_info->copp_id,
+ src_dev_info->copp_id);
+ loopback_status = 1;
+ }
+ }
+ }
+ } else {
+ if (dev_info->opened) {
+ broadcast_event(AUDDEV_EVT_REL_PENDING,
+ route_cfg.dev_id,
+ SESSION_IGNORE);
+ rc = dev_info->dev_ops.close(dev_info);
+ if (rc < 0) {
+ pr_err("%s:Snd device failed close!\n",
+ __func__);
+ return rc;
+ } else {
+ dev_info->opened = 0;
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ route_cfg.dev_id,
+ SESSION_IGNORE);
+ }
+ if (loopback_status == 1) {
+ if ((route_cfg.dev_id == src_dev) ||
+ (route_cfg.dev_id == dst_dev)) {
+ dst_dev_info = audio_dev_ctrl_find_dev(
+ dst_dev);
+ if (IS_ERR(dst_dev_info)) {
+ pr_err("dst_dev:%s:pass invalid"
+ "dev_id\n", __func__);
+ rc = PTR_ERR(dst_dev_info);
+ return rc;
+ }
+ src_dev_info = audio_dev_ctrl_find_dev(
+ src_dev);
+ if (IS_ERR(src_dev_info)) {
+ pr_err("src_dev:%s:pass invalid"
+ "dev_id\n", __func__);
+ rc = PTR_ERR(src_dev_info);
+ return rc;
+ }
+ pr_debug("%d: Disable afe_loopback\n",
+ __LINE__);
+ afe_loopback(LOOPBACK_DISABLE,
+ dst_dev_info->copp_id,
+ src_dev_info->copp_id);
+ loopback_status = 0;
+ }
+ }
+ }
+
+ }
+ return rc;
+}
+
+static int msm_device_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct msm_audio_route_config route_cfg;
+ struct msm_snddev_info *dev_info;
+
+ route_cfg.dev_id = ucontrol->id.numid - device_index;
+ dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
+
+ if (IS_ERR(dev_info)) {
+ pr_err("%s:pass invalid dev_id\n", __func__);
+ rc = PTR_ERR(dev_info);
+ return rc;
+ }
+
+ ucontrol->value.integer.value[0] = dev_info->copp_id;
+ ucontrol->value.integer.value[1] = dev_info->capability;
+
+ return 0;
+}
+
+static int msm_route_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3; /* Device */
+
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = msm_snddev_devcount();
+ return 0;
+}
+
+static int msm_route_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ /* TODO: query Device list */
+ return 0;
+}
+
+static int msm_route_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ int enc_freq = 0;
+ int requested_freq = 0;
+ struct msm_audio_route_config route_cfg;
+ struct msm_snddev_info *dev_info;
+ int session_id = ucontrol->value.integer.value[0];
+ int set = ucontrol->value.integer.value[2];
+ u64 session_mask = 0;
+ route_cfg.dev_id = ucontrol->value.integer.value[1];
+
+ if (ucontrol->id.numid == 2)
+ route_cfg.stream_type = AUDIO_ROUTE_STREAM_PLAYBACK;
+ else
+ route_cfg.stream_type = AUDIO_ROUTE_STREAM_REC;
+
+ pr_debug("%s:route cfg %d %d type for popp %d\n",
+ __func__, route_cfg.dev_id, route_cfg.stream_type, session_id);
+ dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
+
+ if (IS_ERR(dev_info)) {
+ pr_err("%s:pass invalid dev_id\n", __func__);
+ rc = PTR_ERR(dev_info);
+ return rc;
+ }
+ if (route_cfg.stream_type == AUDIO_ROUTE_STREAM_PLAYBACK) {
+ rc = msm_snddev_set_dec(session_id, dev_info->copp_id, set,
+ dev_info->sample_rate, dev_info->channel_mode);
+ session_mask =
+ (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \
+ ((int)AUDDEV_CLNT_DEC-1));
+ if (!set) {
+ if (dev_info->opened)
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ route_cfg.dev_id,
+ session_mask);
+ dev_info->sessions &= ~(session_mask);
+ } else {
+ dev_info->sessions = dev_info->sessions | session_mask;
+ if (dev_info->opened)
+ broadcast_event(AUDDEV_EVT_DEV_RDY,
+ route_cfg.dev_id,
+ session_mask);
+ }
+ } else {
+
+ rc = msm_snddev_set_enc(session_id, dev_info->copp_id, set,
+ dev_info->sample_rate, dev_info->channel_mode);
+ session_mask =
+ (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \
+ ((int)AUDDEV_CLNT_ENC-1));
+ if (!set) {
+ if (dev_info->opened)
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ route_cfg.dev_id,
+ session_mask);
+ dev_info->sessions &= ~(session_mask);
+ } else {
+ dev_info->sessions = dev_info->sessions | session_mask;
+ enc_freq = msm_snddev_get_enc_freq(session_id);
+ requested_freq = enc_freq;
+ if (enc_freq > 0) {
+ rc = msm_snddev_request_freq(&enc_freq,
+ session_id,
+ SNDDEV_CAP_TX,
+ AUDDEV_CLNT_ENC);
+ pr_debug("%s:sample rate configured %d\
+ sample rate requested %d \n",
+ __func__, enc_freq, requested_freq);
+ if ((rc <= 0) || (enc_freq != requested_freq)) {
+ pr_debug("%s:msm_snddev_withdraw_freq\n",
+ __func__);
+ rc = msm_snddev_withdraw_freq
+ (session_id,
+ SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+ broadcast_event(AUDDEV_EVT_FREQ_CHG,
+ route_cfg.dev_id,
+ SESSION_IGNORE);
+ }
+ }
+ if (dev_info->opened)
+ broadcast_event(AUDDEV_EVT_DEV_RDY,
+ route_cfg.dev_id,
+ session_mask);
+ }
+ }
+
+ if (rc < 0) {
+ pr_err("%s:device could not be assigned!\n", __func__);
+ return -EFAULT;
+ }
+
+ return rc;
+}
+
+static int msm_device_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ return 0;
+}
+
+static int msm_device_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_snddev_info *dev_info;
+
+ int dev_id = ucontrol->value.integer.value[0];
+
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+ ucontrol->value.integer.value[0] = dev_info->dev_volume;
+
+ return 0;
+}
+
+static int msm_device_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = -EPERM;
+ struct msm_snddev_info *dev_info;
+
+ int dev_id = ucontrol->value.integer.value[0];
+ int volume = ucontrol->value.integer.value[1];
+
+ pr_debug("%s:dev_id = %d, volume = %d\n", __func__, dev_id, volume);
+
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+
+ if (IS_ERR(dev_info)) {
+ rc = PTR_ERR(dev_info);
+ pr_err("%s: audio_dev_ctrl_find_dev failed. %ld \n",
+ __func__, PTR_ERR(dev_info));
+ return rc;
+ }
+
+ pr_debug("%s:dev_name = %s dev_id = %d, volume = %d\n",
+ __func__, dev_info->name, dev_id, volume);
+
+ if (dev_info->dev_ops.set_device_volume)
+ rc = dev_info->dev_ops.set_device_volume(dev_info, volume);
+ else {
+ pr_info("%s : device %s does not support device volume "
+ "control.", __func__, dev_info->name);
+ return -EPERM;
+ }
+
+ return rc;
+}
+
+static int msm_reset_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0;
+ return 0;
+}
+
+static int msm_reset_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_reset_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_err("%s:Resetting all devices\n", __func__);
+ return msm_reset_all_device();
+}
+
+static int msm_anc_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int msm_anc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_anc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = -EPERM;
+ struct msm_snddev_info *dev_info;
+
+ int dev_id = ucontrol->value.integer.value[0];
+ int enable = ucontrol->value.integer.value[1];
+
+ pr_debug("%s: dev_id = %d, enable = %d\n", __func__, dev_id, enable);
+ dev_info = audio_dev_ctrl_find_dev(dev_id);
+
+ if (IS_ERR(dev_info)) {
+ rc = PTR_ERR(dev_info);
+ pr_err("%s: audio_dev_ctrl_find_dev failed. %ld\n",
+ __func__, PTR_ERR(dev_info));
+ return rc;
+ }
+
+ if (dev_info->dev_ops.enable_anc) {
+ rc = dev_info->dev_ops.enable_anc(dev_info, enable);
+ } else {
+ pr_info("%s : device %s does not support anc control.",
+ __func__, dev_info->name);
+ return -EPERM;
+ }
+
+ return rc;
+}
+
+static int pcm_route_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ /*
+ * First parameter is session id ~ subdevice number
+ * Second parameter is device id.
+ */
+ uinfo->count = 3;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = msm_snddev_devcount();
+ return 0;
+}
+
+static int pcm_route_get_rx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int pcm_route_get_tx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int pcm_route_put_rx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int session_id = 0;
+ int set = 0;
+ struct msm_audio_route_config route_cfg;
+ struct msm_snddev_info *dev_info;
+ u64 session_mask = 0;
+
+ /*
+ * session id is incremented by one and stored as session id 0
+ * is being used by dsp currently. whereas user space would use
+ * subdevice number as session id.
+ */
+ session_id = ucontrol->value.integer.value[0];
+ route_cfg.dev_id = ucontrol->value.integer.value[1];
+ set = ucontrol->value.integer.value[2];
+
+ dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
+ if (IS_ERR(dev_info)) {
+ pr_err("pass invalid dev_id %d\n", route_cfg.dev_id);
+ return PTR_ERR(dev_info);
+ }
+ if (!(dev_info->capability & SNDDEV_CAP_RX))
+ return -EINVAL;
+ session_mask =
+ (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \
+ ((int)AUDDEV_CLNT_DEC-1));
+ if (!set) {
+ session_route.playback_session[session_id][dev_info->copp_id]
+ = DEVICE_IGNORE;
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ route_cfg.dev_id,
+ session_mask);
+ dev_info->sessions &= ~(session_mask);
+ return 0;
+ }
+ pr_debug("%s:Routing playback session %d to %s\n",
+ __func__, (session_id),
+ dev_info->name);
+ session_route.playback_session[session_id][dev_info->copp_id] =
+ dev_info->copp_id;
+ if (dev_info->opened) {
+ dev_info->sessions = dev_info->sessions | session_mask;
+ broadcast_event(AUDDEV_EVT_DEV_RDY,
+ route_cfg.dev_id,
+ session_mask);
+ } else {
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ route_cfg.dev_id,
+ session_mask);
+ dev_info->sessions &= ~(session_mask);
+ }
+ return 0;
+}
+
+static int pcm_route_put_tx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int session_id = 0;
+ int set = 0;
+ struct msm_audio_route_config route_cfg;
+ struct msm_snddev_info *dev_info;
+ u64 session_mask = 0;
+
+ session_id = ucontrol->value.integer.value[0];
+ route_cfg.dev_id = ucontrol->value.integer.value[1];
+ set = ucontrol->value.integer.value[2];
+
+ dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
+ if (IS_ERR(dev_info)) {
+ pr_err("pass invalid dev_id %d\n", route_cfg.dev_id);
+ return PTR_ERR(dev_info);
+ }
+ pr_debug("%s:Routing capture session %d to %s\n", __func__,
+ session_id,
+ dev_info->name);
+ if (!(dev_info->capability & SNDDEV_CAP_TX))
+ return -EINVAL;
+ session_mask =
+ (((u64)0x1) << session_id) << (MAX_BIT_PER_CLIENT * \
+ ((int)AUDDEV_CLNT_ENC-1));
+ if (!set) {
+ session_route.capture_session[session_id][dev_info->copp_id]
+ = DEVICE_IGNORE;
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ route_cfg.dev_id,
+ session_mask);
+ dev_info->sessions &= ~(session_mask);
+ return 0;
+ }
+
+ session_route.capture_session[session_id][dev_info->copp_id] =
+ dev_info->copp_id;
+ if (dev_info->opened) {
+ dev_info->sessions = dev_info->sessions | session_mask;
+ broadcast_event(AUDDEV_EVT_DEV_RDY,
+ route_cfg.dev_id,
+ session_mask);
+ } else {
+ broadcast_event(AUDDEV_EVT_DEV_RLS,
+ route_cfg.dev_id,
+ session_mask);
+ dev_info->sessions &= ~(session_mask);
+ }
+ return 0;
+}
+
+static int msm_loopback_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = msm_snddev_devcount();
+ return 0;
+}
+
+static int msm_loopback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_loopback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int rc = 0;
+ struct msm_snddev_info *src_dev_info = NULL; /* TX device */
+ struct msm_snddev_info *dst_dev_info = NULL; /* RX device */
+ int dst_dev_id = ucontrol->value.integer.value[0];
+ int src_dev_id = ucontrol->value.integer.value[1];
+ int set = ucontrol->value.integer.value[2];
+
+ pr_debug("%s: dst=%d :src=%d set=%d\n", __func__,
+ dst_dev_id, src_dev_id, set);
+
+ dst_dev_info = audio_dev_ctrl_find_dev(dst_dev_id);
+ if (IS_ERR(dst_dev_info)) {
+ pr_err("dst_dev:%s:pass invalid dev_id\n", __func__);
+ rc = PTR_ERR(dst_dev_info);
+ return rc;
+ }
+ if (!(dst_dev_info->capability & SNDDEV_CAP_RX)) {
+ pr_err("Destination device %d is not RX device\n",
+ dst_dev_id);
+ return -EFAULT;
+ }
+
+ src_dev_info = audio_dev_ctrl_find_dev(src_dev_id);
+ if (IS_ERR(src_dev_info)) {
+ pr_err("src_dev:%s:pass invalid dev_id\n", __func__);
+ rc = PTR_ERR(src_dev_info);
+ return rc;
+ }
+ if (!(src_dev_info->capability & SNDDEV_CAP_TX)) {
+ pr_err("Source device %d is not TX device\n", src_dev_id);
+ return -EFAULT;
+ }
+
+ if (set) {
+ pr_debug("%s:%d:Enabling AFE_Loopback\n", __func__, __LINE__);
+ src_dev = src_dev_id;
+ dst_dev = dst_dev_id;
+ loopback_status = 1;
+ if ((dst_dev_info->opened) && (src_dev_info->opened))
+ rc = afe_loopback(LOOPBACK_ENABLE,
+ dst_dev_info->copp_id,
+ src_dev_info->copp_id);
+ } else {
+ pr_debug("%s:%d:Disabling AFE_Loopback\n", __func__, __LINE__);
+ src_dev = DEVICE_IGNORE;
+ dst_dev = DEVICE_IGNORE;
+ loopback_status = 0;
+ rc = afe_loopback(LOOPBACK_DISABLE,
+ dst_dev_info->copp_id,
+ src_dev_info->copp_id);
+ }
+ return rc;
+}
+
+static struct snd_kcontrol_new snd_dev_controls[AUDIO_DEV_CTL_MAX_DEV];
+
+static int snd_dev_ctl_index(int idx)
+{
+ struct msm_snddev_info *dev_info;
+
+ dev_info = audio_dev_ctrl_find_dev(idx);
+ if (IS_ERR(dev_info)) {
+ pr_err("%s:pass invalid dev_id\n", __func__);
+ return PTR_ERR(dev_info);
+ }
+ if (sizeof(dev_info->name) <= 44)
+ sprintf(&snddev_name[idx][0] , "%s", dev_info->name);
+
+ snd_dev_controls[idx].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ snd_dev_controls[idx].access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ snd_dev_controls[idx].name = &snddev_name[idx][0];
+ snd_dev_controls[idx].index = idx;
+ snd_dev_controls[idx].info = msm_device_info;
+ snd_dev_controls[idx].get = msm_device_get;
+ snd_dev_controls[idx].put = msm_device_put;
+ snd_dev_controls[idx].private_value = 0;
+ return 0;
+
+}
+
+#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .name = xname, \
+ .info = fp_info,\
+ .get = fp_get, .put = fp_put, \
+ .private_value = addr, \
+}
+
+/* If new controls are to be added which would be constant across the
+ * different targets, please add to the structure
+ * snd_msm_controls. Please do not add any controls to the structure
+ * snd_msm_secondary_controls defined below unless they are msm8x60
+ * specific.
+ */
+
+static struct snd_kcontrol_new snd_msm_controls[] = {
+ MSM_EXT("Count", msm_scontrol_count_info, msm_scontrol_count_get, \
+ NULL, 0),
+ MSM_EXT("Stream", msm_route_info, msm_route_get, \
+ msm_route_put, 0),
+ MSM_EXT("Record", msm_route_info, msm_route_get, \
+ msm_route_put, 0),
+ MSM_EXT("Voice", msm_voice_info, msm_voice_get, \
+ msm_voice_put, 0),
+ MSM_EXT("Volume", msm_volume_info, msm_volume_get, \
+ msm_volume_put, 0),
+ MSM_EXT("VoiceVolume", msm_v_volume_info, msm_v_volume_get, \
+ msm_v_volume_put, 0),
+ MSM_EXT("VoiceMute", msm_v_mute_info, msm_v_mute_get, \
+ msm_v_mute_put, 0),
+ MSM_EXT("Voice Call", msm_v_call_info, msm_v_call_get, \
+ msm_v_call_put, 0),
+ MSM_EXT("Device_Volume", msm_device_volume_info,
+ msm_device_volume_get, msm_device_volume_put, 0),
+ MSM_EXT("Reset", msm_reset_info,
+ msm_reset_get, msm_reset_put, 0),
+ MSM_EXT("ANC", msm_anc_info, msm_anc_get, msm_anc_put, 0),
+};
+
+static struct snd_kcontrol_new snd_msm_secondary_controls[] = {
+ MSM_EXT("PCM Playback Sink",
+ pcm_route_info, pcm_route_get_rx, pcm_route_put_rx, 0),
+ MSM_EXT("PCM Capture Source",
+ pcm_route_info, pcm_route_get_tx, pcm_route_put_tx, 0),
+ MSM_EXT("Sound Device Loopback", msm_loopback_info,
+ msm_loopback_get, msm_loopback_put, 0),
+};
+
+static int msm_new_mixer(struct snd_soc_codec *codec)
+{
+ unsigned int idx;
+ int err;
+ int dev_cnt;
+
+ strcpy(codec->card->snd_card->mixername, "MSM Mixer");
+ for (idx = 0; idx < ARRAY_SIZE(snd_msm_controls); idx++) {
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&snd_msm_controls[idx],
+ NULL));
+ if (err < 0)
+ pr_err("%s:ERR adding ctl\n", __func__);
+ }
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_msm_secondary_controls); idx++) {
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&snd_msm_secondary_controls[idx],
+ NULL));
+ if (err < 0)
+ pr_err("%s:ERR adding secondary ctl\n", __func__);
+ }
+ dev_cnt = msm_snddev_devcount();
+
+ for (idx = 0; idx < dev_cnt; idx++) {
+ if (!snd_dev_ctl_index(idx)) {
+ err = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&snd_dev_controls[idx],
+ NULL));
+ if (err < 0)
+ pr_err("%s:ERR adding ctl\n", __func__);
+ } else
+ return 0;
+ }
+ simple_control = ARRAY_SIZE(snd_msm_controls)
+ + ARRAY_SIZE(snd_msm_secondary_controls);
+ device_index = simple_control + 1;
+ return 0;
+}
+
+static int msm_soc_dai_init(
+ struct snd_soc_pcm_runtime *rtd)
+{
+
+ int ret = 0;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ init_waitqueue_head(&the_locks.enable_wait);
+ init_waitqueue_head(&the_locks.eos_wait);
+ init_waitqueue_head(&the_locks.write_wait);
+ init_waitqueue_head(&the_locks.read_wait);
+ memset(&session_route, DEVICE_IGNORE, sizeof(struct pcm_session));
+
+ ret = msm_new_mixer(codec);
+ if (ret < 0)
+ pr_err("%s: ALSA MSM Mixer Fail\n", __func__);
+
+ return ret;
+}
+
+static struct snd_soc_dai_link msm_dai[] = {
+{
+ .name = "MSM Primary I2S",
+ .stream_name = "DSP 1",
+ .cpu_dai_name = "msm-cpu-dai.0",
+ .platform_name = "msm-dsp-audio.0",
+ .codec_name = "msm-codec-dai.0",
+ .codec_dai_name = "msm-codec-dai",
+ .init = &msm_soc_dai_init,
+},
+#ifdef CONFIG_MSM_8x60_VOIP
+{
+ .name = "MSM Primary Voip",
+ .stream_name = "MVS",
+ .cpu_dai_name = "mvs-cpu-dai.0",
+ .platform_name = "msm-mvs-audio.0",
+ .codec_name = "mvs-codec-dai.0",
+ .codec_dai_name = "mvs-codec-dai",
+},
+#endif
+};
+
+static struct snd_soc_card snd_soc_card_msm = {
+ .name = "msm-audio",
+ .dai_link = msm_dai,
+ .num_links = ARRAY_SIZE(msm_dai),
+};
+
+static int __init msm_audio_init(void)
+{
+ int ret;
+
+ msm_audio_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!msm_audio_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(msm_audio_snd_device, &snd_soc_card_msm);
+ ret = platform_device_add(msm_audio_snd_device);
+ if (ret) {
+ platform_device_put(msm_audio_snd_device);
+ return ret;
+ }
+
+ src_dev = DEVICE_IGNORE;
+ dst_dev = DEVICE_IGNORE;
+
+ return ret;
+}
+
+static void __exit msm_audio_exit(void)
+{
+ platform_device_unregister(msm_audio_snd_device);
+}
+
+module_init(msm_audio_init);
+module_exit(msm_audio_exit);
+
+MODULE_DESCRIPTION("PCM module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm_audio_mvs.h b/sound/soc/msm/msm_audio_mvs.h
new file mode 100644
index 0000000..728621a
--- /dev/null
+++ b/sound/soc/msm/msm_audio_mvs.h
@@ -0,0 +1,368 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+
+#ifndef __MSM_AUDIO_MVS_H
+#define __MSM_AUDIO_MVS_H
+#include <linux/msm_audio.h>
+#include <linux/wakelock.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/debug_mm.h>
+#include <linux/slab.h>
+
+
+#define AUDIO_GET_MVS_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM + 0), unsigned)
+#define AUDIO_SET_MVS_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM + 1), unsigned)
+#define AUDIO_SET_SCR_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM + 2), unsigned)
+#define AUDIO_SET_DTX_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \
+ (AUDIO_MAX_COMMON_IOCTL_NUM + 3), unsigned)
+/* MVS modes */
+#define MVS_MODE_LINEAR_PCM 9
+
+#define MVS_PROG 0x30000014
+#define MVS_VERS 0x00030001
+
+#define MVS_CLIENT_ID_VOIP 0x00000003 /* MVS_CLIENT_VOIP */
+
+#define MVS_ACQUIRE_PROC 4
+#define MVS_ENABLE_PROC 5
+#define MVS_RELEASE_PROC 6
+#define MVS_SET_PCM_MODE_PROC 9
+
+#define MVS_EVENT_CB_TYPE_PROC 1
+#define MVS_PACKET_UL_FN_TYPE_PROC 2
+#define MVS_PACKET_DL_FN_TYPE_PROC 3
+
+#define MVS_CB_FUNC_ID 0xAAAABBBB
+#define MVS_UL_CB_FUNC_ID 0xBBBBCCCC
+#define MVS_DL_CB_FUNC_ID 0xCCCCDDDD
+
+/* MVS frame modes */
+
+#define MVS_FRAME_MODE_PCM_UL 13
+#define MVS_FRAME_MODE_PCM_DL 14
+
+/* MVS context */
+#define MVS_PKT_CONTEXT_ISR 0x00000001
+
+/* Max voc packet size */
+#define MVS_MAX_VOC_PKT_SIZE 320
+
+#define VOIP_MAX_Q_LEN 20
+#define MVS_MAX_Q_LEN 8
+#define RPC_TYPE_REQUEST 0
+#define RPC_TYPE_REPLY 1
+
+#define RPC_STATUS_FAILURE 0
+#define RPC_STATUS_SUCCESS 1
+#define RPC_STATUS_REJECT 1
+
+
+#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
+#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
+#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
+
+
+enum audio_mvs_state_type { AUDIO_MVS_CLOSED, AUDIO_MVS_OPENED,
+ AUDIO_MVS_PREPARING, AUDIO_MVS_ACQUIRE, AUDIO_MVS_ENABLED,
+ AUDIO_MVS_CLOSING
+};
+
+enum audio_mvs_event_type { AUDIO_MVS_COMMAND, AUDIO_MVS_MODE,
+ AUDIO_MVS_NOTIFY
+};
+
+enum audio_mvs_cmd_status_type { AUDIO_MVS_CMD_FAILURE, AUDIO_MVS_CMD_BUSY,
+ AUDIO_MVS_CMD_SUCCESS
+};
+
+enum audio_mvs_mode_status_type { AUDIO_MVS_MODE_NOT_AVAIL,
+ AUDIO_MVS_MODE_INIT, AUDIO_MVS_MODE_READY
+};
+
+enum audio_mvs_pkt_status_type { AUDIO_MVS_PKT_NORMAL, AUDIO_MVS_PKT_FAST,
+ AUDIO_MVS_PKT_SLOW
+};
+
+struct rpc_audio_mvs_acquire_args {
+ uint32_t client_id;
+ uint32_t cb_func_id;
+};
+
+struct audio_mvs_acquire_msg {
+ struct rpc_request_hdr rpc_hdr;
+ struct rpc_audio_mvs_acquire_args acquire_args;
+};
+
+struct rpc_audio_mvs_enable_args {
+ uint32_t client_id;
+ uint32_t mode;
+ uint32_t ul_cb_func_id;
+ uint32_t dl_cb_func_id;
+ uint32_t context;
+};
+
+struct audio_mvs_enable_msg {
+ struct rpc_request_hdr rpc_hdr;
+ struct rpc_audio_mvs_enable_args enable_args;
+};
+
+struct audio_mvs_release_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t client_id;
+};
+
+struct audio_mvs_set_pcm_mode_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t pcm_mode;
+};
+
+struct audio_mvs_set_pcmwb_mode_msg {
+ struct rpc_request_hdr rpc_hdr;
+ uint32_t pcmwb_mode;
+};
+
+struct audio_mvs_buffer {
+ uint8_t *voc_pkt;
+ uint32_t len;
+};
+
+union audio_mvs_event_data {
+ struct mvs_ev_command_type {
+ uint32_t event;
+ uint32_t client_id;
+ uint32_t cmd_status;
+ } mvs_ev_command_type;
+
+ struct mvs_ev_mode_type {
+ uint32_t event;
+ uint32_t client_id;
+ uint32_t mode_status;
+ uint32_t mode;
+ } mvs_ev_mode_type;
+
+ struct mvs_ev_notify_type {
+ uint32_t event;
+ uint32_t client_id;
+ uint32_t buf_dir;
+ uint32_t max_frames;
+ } mvs_ev_notify_type;
+};
+
+struct audio_mvs_cb_func_args {
+ uint32_t cb_func_id;
+ uint32_t valid_ptr;
+ uint32_t event;
+ union audio_mvs_event_data event_data;
+};
+
+struct audio_mvs_frame_info_hdr {
+ uint32_t frame_mode;
+ uint32_t mvs_mode;
+ uint32_t buf_free_cnt;
+};
+
+struct audio_mvs_ul_cb_func_args {
+ uint32_t cb_func_id;
+ uint32_t pkt_len;
+ uint32_t voc_pkt[MVS_MAX_VOC_PKT_SIZE / 4];
+
+ uint32_t valid_ptr;
+
+ uint32_t frame_mode;
+ uint32_t frame_mode_ignore;
+
+ struct audio_mvs_frame_info_hdr frame_info_hdr;
+
+ uint32_t pcm_frame;
+ uint32_t pcm_mode;
+
+ uint32_t pkt_len_ignore;
+};
+
+struct audio_mvs_ul_reply {
+ struct rpc_reply_hdr reply_hdr;
+ uint32_t valid_pkt_status_ptr;
+ uint32_t pkt_status;
+};
+
+struct audio_mvs_dl_cb_func_args {
+ uint32_t cb_func_id;
+ uint32_t valid_ptr;
+
+ uint32_t frame_mode;
+ uint32_t frame_mode_ignore;
+
+ struct audio_mvs_frame_info_hdr frame_info_hdr;
+
+ uint32_t pcm_frame;
+ uint32_t pcm_mode;
+
+};
+
+struct audio_mvs_dl_reply {
+ struct rpc_reply_hdr reply_hdr;
+ uint32_t voc_pkt[MVS_MAX_VOC_PKT_SIZE / 4];
+ uint32_t valid_frame_info_ptr;
+
+ uint32_t frame_mode;
+ uint32_t frame_mode_again;
+
+ struct audio_mvs_frame_info_hdr frame_info_hdr;
+
+ uint32_t pcm_frame;
+ uint32_t pcm_mode;
+
+ uint32_t valid_pkt_status_ptr;
+ uint32_t pkt_status;
+};
+
+struct audio_mvs_info_type {
+ enum audio_mvs_state_type state;
+ uint32_t frame_mode;
+ uint32_t mvs_mode;
+ uint32_t buf_free_cnt;
+ uint32_t pcm_frame;
+ uint32_t pcm_mode;
+ uint32_t out_sample_rate;
+ uint32_t out_channel_mode;
+ uint32_t out_weight;
+ uint32_t out_buffer_size;
+ int dl_play;
+ struct msm_rpc_endpoint *rpc_endpt;
+ uint32_t rpc_prog;
+ uint32_t rpc_ver;
+ uint32_t rpc_status;
+
+ unsigned int pcm_size;
+ unsigned int pcm_count;
+ unsigned int pcm_playback_irq_pos; /* IRQ position */
+ unsigned int pcm_playback_buf_pos; /* position in buffer */
+
+ unsigned int pcm_capture_size;
+ unsigned int pcm_capture_count;
+ unsigned int pcm_capture_irq_pos; /* IRQ position */
+ unsigned int pcm_capture_buf_pos; /* position in buffer */
+
+ uint32_t samp_rate;
+ uint32_t channel_mode;
+
+ uint8_t *mem_chunk;
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ struct audio_mvs_buffer in[MVS_MAX_Q_LEN];
+ uint32_t in_read;
+ uint32_t in_write;
+
+ struct audio_mvs_buffer out[MVS_MAX_Q_LEN];
+ uint32_t out_read;
+ uint32_t out_write;
+
+ struct task_struct *task;
+
+ wait_queue_head_t wait;
+ wait_queue_head_t prepare_wait;
+ wait_queue_head_t out_wait;
+ wait_queue_head_t in_wait;
+
+
+ struct mutex lock;
+ struct mutex prepare_lock;
+ struct mutex in_lock;
+ struct mutex out_lock;
+
+ struct wake_lock suspend_lock;
+ struct wake_lock idle_lock;
+ struct timer_list timer;
+ unsigned long expiry;
+ int ack_dl_count;
+ int ack_ul_count;
+ int prepare_ack;
+ int playback_start;
+ int capture_start;
+ unsigned long expiry_delta;
+ int mvs_enable;
+ int playback_enable;
+ int capture_enable;
+ int instance;
+
+};
+
+struct audio_voip_info_type {
+ enum audio_mvs_state_type state;
+ enum audio_mvs_state_type playback_state;
+ enum audio_mvs_state_type capture_state;
+
+ unsigned int pcm_playback_size;
+ unsigned int pcm_count;
+ unsigned int pcm_playback_irq_pos; /* IRQ position */
+ unsigned int pcm_playback_buf_pos; /* position in buffer */
+
+ unsigned int pcm_capture_size;
+ unsigned int pcm_capture_count;
+ unsigned int pcm_capture_irq_pos; /* IRQ position */
+ unsigned int pcm_capture_buf_pos; /* position in buffer */
+
+ struct snd_pcm_substream *playback_substream;
+ struct snd_pcm_substream *capture_substream;
+
+ struct audio_mvs_buffer in[VOIP_MAX_Q_LEN];
+ uint32_t in_read;
+ uint32_t in_write;
+
+ struct audio_mvs_buffer out[VOIP_MAX_Q_LEN];
+ uint32_t out_read;
+ uint32_t out_write;
+
+ wait_queue_head_t out_wait;
+ wait_queue_head_t in_wait;
+
+ struct mutex lock;
+ struct mutex prepare_lock;
+
+ struct wake_lock suspend_lock;
+ struct wake_lock idle_lock;
+ int playback_start;
+ int capture_start;
+ int instance;
+};
+
+enum msm_audio_pcm_frame_type {
+ MVS_AMR_SPEECH_GOOD, /* Good speech frame */
+ MVS_AMR_SPEECH_DEGRADED, /* Speech degraded */
+ MVS_AMR_ONSET, /* onset */
+ MVS_AMR_SPEECH_BAD, /* Corrupt speech frame (bad CRC) */
+ MVS_AMR_SID_FIRST, /* First silence descriptor */
+ MVS_AMR_SID_UPDATE, /* Comfort noise frame */
+ MVS_AMR_SID_BAD, /* Corrupt SID frame (bad CRC) */
+ MVS_AMR_NO_DATA, /* Nothing to transmit */
+ MVS_AMR_SPEECH_LOST, /* downlink speech lost */
+};
+
+enum msm_audio_dtx_mode_type { MVS_DTX_OFF, MVS_DTX_ON
+};
+
+struct msm_audio_mvs_config {
+ uint32_t mvs_mode;
+ uint32_t bit_rate;
+};
+
+extern struct snd_soc_dai_driver msm_mvs_dais[2];
+extern struct snd_soc_codec_device soc_codec_dev_msm_mvs;
+extern struct snd_soc_platform_driver msm_mvs_soc_platform;
+extern struct snd_soc_platform_driver msm_voip_soc_platform;
+#endif /* __MSM_AUDIO_MVS_H */
diff --git a/sound/soc/msm/mvs-dai.c b/sound/soc/msm/mvs-dai.c
new file mode 100644
index 0000000..521c5e5
--- /dev/null
+++ b/sound/soc/msm/mvs-dai.c
@@ -0,0 +1,141 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include "msm_audio_mvs.h"
+
+static struct snd_soc_dai_driver msm_mvs_codec_dais[] = {
+{
+ .name = "mvs-codec-dai",
+ .playback = {
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_8000),
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_8000),
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+static struct snd_soc_dai_driver msm_mvs_cpu_dais[] = {
+{
+ .name = "mvs-cpu-dai",
+ .playback = {
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_8000),
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_8000),
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_msm = {
+ .compress_type = SND_SOC_FLAT_COMPRESSION,
+};
+
+static __devinit int asoc_mvs_codec_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm,
+ msm_mvs_codec_dais, ARRAY_SIZE(msm_mvs_codec_dais));
+}
+
+static int __devexit asoc_mvs_codec_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static __devinit int asoc_mvs_cpu_probe(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ return snd_soc_register_dai(&pdev->dev, msm_mvs_cpu_dais);
+}
+
+static int __devexit asoc_mvs_cpu_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dai(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver asoc_mvs_codec_driver = {
+ .probe = asoc_mvs_codec_probe,
+ .remove = __devexit_p(asoc_mvs_codec_remove),
+ .driver = {
+ .name = "mvs-codec-dai",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_driver asoc_mvs_cpu_driver = {
+ .probe = asoc_mvs_cpu_probe,
+ .remove = __devexit_p(asoc_mvs_cpu_remove),
+ .driver = {
+ .name = "mvs-cpu-dai",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mvs_codec_dai_init(void)
+{
+ return platform_driver_register(&asoc_mvs_codec_driver);
+}
+
+static void __exit mvs_codec_dai_exit(void)
+{
+ platform_driver_unregister(&asoc_mvs_codec_driver);
+}
+
+static int __init mvs_cpu_dai_init(void)
+{
+ return platform_driver_register(&asoc_mvs_cpu_driver);
+}
+
+static void __exit mvs_cpu_dai_exit(void)
+{
+ platform_driver_unregister(&asoc_mvs_cpu_driver);
+}
+
+module_init(mvs_codec_dai_init);
+module_exit(mvs_codec_dai_exit);
+module_init(mvs_cpu_dai_init);
+module_exit(mvs_cpu_dai_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM Codec/Cpu Dai driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6/Makefile b/sound/soc/msm/qdsp6/Makefile
new file mode 100644
index 0000000..5fd69cc
--- /dev/null
+++ b/sound/soc/msm/qdsp6/Makefile
@@ -0,0 +1 @@
+obj-y := q6asm.o q6adm.o q6afe.o q6voice.o
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
new file mode 100644
index 0000000..65d1fbc
--- /dev/null
+++ b/sound/soc/msm/qdsp6/q6adm.c
@@ -0,0 +1,804 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+#include <linux/bitops.h>
+#include <mach/qdsp6v2/rtac.h>
+#include <mach/qdsp6v2/audio_acdb.h>
+#include <sound/apr_audio.h>
+#include <sound/q6afe.h>
+#include <mach/qdsp6v2/audio_dev_ctl.h>
+
+#define TIMEOUT_MS 1000
+#define AUDIO_RX 0x0
+#define AUDIO_TX 0x1
+#define ASM_MAX_SESSION 0x8 /* To do: define in a header */
+#define RESET_COPP_ID 99
+#define INVALID_COPP_ID 0xFF
+
+struct adm_ctl {
+ void *apr;
+ atomic_t copp_id[AFE_MAX_PORTS];
+ atomic_t copp_cnt[AFE_MAX_PORTS];
+ atomic_t copp_stat[AFE_MAX_PORTS];
+ unsigned long sessions[AFE_MAX_PORTS];
+ wait_queue_head_t wait;
+};
+
+static struct adm_ctl this_adm;
+
+static int32_t adm_callback(struct apr_client_data *data, void *priv)
+{
+ uint32_t *payload;
+ int i, index;
+ payload = data->payload;
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("adm_callback: Reset event is received: %d %d apr[%p]\n",
+ data->reset_event, data->reset_proc,
+ this_adm.apr);
+ if (this_adm.apr) {
+ apr_reset(this_adm.apr);
+ for (i = 0; i < AFE_MAX_PORTS; i++) {
+ atomic_set(&this_adm.copp_id[i],
+ RESET_COPP_ID);
+ atomic_set(&this_adm.copp_cnt[i], 0);
+ atomic_set(&this_adm.copp_stat[i], 0);
+ }
+ this_adm.apr = NULL;
+ }
+ return 0;
+ }
+
+ pr_debug("%s: code = 0x%x %x %x size = %d\n", __func__,
+ data->opcode, payload[0], payload[1],
+ data->payload_size);
+
+ if (data->payload_size) {
+ index = afe_get_port_index(data->token);
+ pr_debug("%s: Port ID %d, index %d\n", __func__,
+ data->token, index);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ pr_debug("APR_BASIC_RSP_RESULT\n");
+ switch (payload[0]) {
+ case ADM_CMD_SET_PARAMS:
+#ifdef CONFIG_MSM8X60_RTAC
+ if (rtac_make_adm_callback(payload,
+ data->payload_size))
+ break;
+#endif
+ case ADM_CMD_COPP_CLOSE:
+ case ADM_CMD_MEMORY_MAP:
+ case ADM_CMD_MEMORY_UNMAP:
+ case ADM_CMD_MEMORY_MAP_REGIONS:
+ case ADM_CMD_MEMORY_UNMAP_REGIONS:
+ case ADM_CMD_MATRIX_MAP_ROUTINGS:
+ pr_debug("ADM_CMD_MATRIX_MAP_ROUTINGS\n");
+ atomic_set(&this_adm.copp_stat[index], 1);
+ wake_up(&this_adm.wait);
+ break;
+ default:
+ pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
+ payload[0]);
+ break;
+ }
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case ADM_CMDRSP_COPP_OPEN: {
+ struct adm_copp_open_respond *open = data->payload;
+ if (open->copp_id == INVALID_COPP_ID) {
+ pr_err("%s: invalid coppid rxed %d\n",
+ __func__, open->copp_id);
+ atomic_set(&this_adm.copp_stat[index], 1);
+ wake_up(&this_adm.wait);
+ break;
+ }
+ atomic_set(&this_adm.copp_id[index], open->copp_id);
+ atomic_set(&this_adm.copp_stat[index], 1);
+ pr_debug("%s: coppid rxed=%d\n", __func__,
+ open->copp_id);
+ wake_up(&this_adm.wait);
+ }
+ break;
+#ifdef CONFIG_MSM8X60_RTAC
+ case ADM_CMDRSP_GET_PARAMS:
+ pr_debug("ADM_CMDRSP_GET_PARAMS\n");
+ rtac_make_adm_callback(payload,
+ data->payload_size);
+ break;
+#endif
+ default:
+ pr_err("%s: Unknown cmd:0x%x\n", __func__,
+ data->opcode);
+ break;
+ }
+ }
+ return 0;
+}
+
+void send_cal(int port_id, struct acdb_cal_block *aud_cal)
+{
+ s32 result;
+ struct adm_set_params_command adm_params;
+ int index = afe_get_port_index(port_id);
+
+ pr_debug("%s: Port id %d, index %d\n", __func__, port_id, index);
+
+ if (!aud_cal || aud_cal->cal_size == 0) {
+ pr_err("%s: No calibration data to send!\n", __func__);
+ goto done;
+ }
+
+ adm_params.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ adm_params.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(adm_params));
+ adm_params.hdr.src_svc = APR_SVC_ADM;
+ adm_params.hdr.src_domain = APR_DOMAIN_APPS;
+ adm_params.hdr.src_port = port_id;
+ adm_params.hdr.dest_svc = APR_SVC_ADM;
+ adm_params.hdr.dest_domain = APR_DOMAIN_ADSP;
+ adm_params.hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ adm_params.hdr.token = port_id;
+ adm_params.hdr.opcode = ADM_CMD_SET_PARAMS;
+ adm_params.payload = aud_cal->cal_paddr;
+ adm_params.payload_size = aud_cal->cal_size;
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+ pr_debug("%s: Sending SET_PARAMS payload = 0x%x, size = %d\n",
+ __func__, adm_params.payload, adm_params.payload_size);
+ result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_params);
+ if (result < 0) {
+ pr_err("%s: Set params failed port = %d payload = 0x%x\n",
+ __func__, port_id, aud_cal->cal_paddr);
+ goto done;
+ }
+ /* Wait for the callback */
+ result = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!result)
+ pr_err("%s: Set params timed out port = %d, payload = 0x%x\n",
+ __func__, port_id, aud_cal->cal_paddr);
+done:
+ return;
+}
+
+void send_adm_cal(int port_id, int path)
+{
+ s32 acdb_path;
+ struct acdb_cal_block aud_cal;
+
+ pr_debug("%s\n", __func__);
+
+ /* Maps audio_dev_ctrl path definition to ACDB definition */
+ acdb_path = path - 1;
+ if ((acdb_path >= NUM_AUDPROC_BUFFERS) ||
+ (acdb_path < 0)) {
+ pr_err("%s: Path is not RX or TX, path = %d\n",
+ __func__, path);
+ goto done;
+ }
+
+ pr_debug("%s: Sending audproc cal\n", __func__);
+ get_audproc_cal(acdb_path, &aud_cal);
+ send_cal(port_id, &aud_cal);
+
+ pr_debug("%s: Sending audvol cal\n", __func__);
+ get_audvol_cal(acdb_path, &aud_cal);
+ send_cal(port_id, &aud_cal);
+done:
+ return;
+}
+
+/* This function issues routing command of ASM stream
+ * to ADM mixer associated with a particular AFE port
+ */
+int adm_cmd_map(int port_id, int session_id)
+{
+ struct adm_routings_command route;
+ int ret = 0;
+ int index = afe_get_port_index(port_id);
+
+ pr_debug("%s: port %x session %x\n", __func__, port_id, session_id);
+
+ if (!atomic_read(&this_adm.copp_cnt[index]))
+ return 0;
+
+ route.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ route.hdr.pkt_size = sizeof(route);
+ route.hdr.src_svc = 0;
+ route.hdr.src_domain = APR_DOMAIN_APPS;
+ route.hdr.src_port = port_id;
+ route.hdr.dest_svc = APR_SVC_ADM;
+ route.hdr.dest_domain = APR_DOMAIN_ADSP;
+ route.hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ route.hdr.token = port_id;
+ route.hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS;
+ route.num_sessions = 1;
+ route.session[0].id = session_id;
+ route.session[0].num_copps = 1;
+ route.session[0].copp_id[0] =
+ atomic_read(&this_adm.copp_id[index]);
+
+ /* This rule can change */
+ if ((port_id & 0x1))
+ route.path = AUDIO_TX;
+ else
+ route.path = AUDIO_RX;
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&route);
+ if (ret < 0) {
+ pr_err("%s: ADM routing for port %d failed\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: ADM cmd Route failed for port %d\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ }
+
+fail_cmd:
+ return ret;
+}
+
+/* This function establish routing of ASM stream to a particular
+ * ADM mixer that is routed to a particular hardware port
+ * session id must be in range of 0 ~ 31.
+ */
+int adm_route_session(int port_id, uint session_id, int set)
+{
+ int rc = 0;
+ int index;
+
+ pr_debug("%s: port %x session %x set %x\n", __func__,
+ port_id, session_id, set);
+
+ index = afe_get_port_index(port_id);
+
+ if (index >= AFE_MAX_PORTS) {
+ pr_err("%s port idi[%d] out of limit[%d]\n", __func__,
+ port_id, AFE_MAX_PORTS);
+ return -ENODEV;
+ }
+
+ if (set) {
+ set_bit(session_id, &this_adm.sessions[index]);
+ rc = adm_cmd_map(port_id, session_id); /* not thread safe */
+ } else /* Not sure how to deroute yet */
+ clear_bit(session_id, &this_adm.sessions[index]);
+
+ return rc;
+}
+
+/* This function instantiates a mixer in QDSP6 audio path for
+ * given audio hardware port. Topology should be made part
+ * of audio calibration
+ */
+int adm_open_mixer(int port_id, int path, int rate,
+ int channel_mode, int topology) {
+ struct adm_copp_open_command open;
+ int ret = 0;
+ u32 i;
+ int index;
+
+ pr_debug("%s: port %d path:%d rate:%d mode:%d\n", __func__,
+ port_id, path, rate, channel_mode);
+
+ if (afe_validate_port(port_id) < 0) {
+ pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+ return -ENODEV;
+ }
+
+ index = afe_get_port_index(port_id);
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+
+ if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
+
+ open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ open.hdr.pkt_size = sizeof(open);
+ open.hdr.src_svc = APR_SVC_ADM;
+ open.hdr.src_domain = APR_DOMAIN_APPS;
+ open.hdr.src_port = port_id;
+ open.hdr.dest_svc = APR_SVC_ADM;
+ open.hdr.dest_domain = APR_DOMAIN_ADSP;
+ open.hdr.dest_port = port_id;
+ open.hdr.token = port_id;
+ open.hdr.opcode = ADM_CMD_COPP_OPEN;
+
+ open.mode = path;
+ open.endpoint_id1 = port_id;
+ open.endpoint_id2 = 0xFFFF;
+
+ open.topology_id = get_adm_topology();
+ if (open.topology_id == 0)
+ open.topology_id = topology;
+
+ open.channel_config = channel_mode & 0x00FF;
+ open.rate = rate;
+
+ pr_debug("%s: channel_config=%d port_id=%d rate=%d\
+ topology_id=0x%X\n", __func__, open.channel_config,\
+ open.endpoint_id1, open.rate,\
+ open.topology_id);
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
+ if (ret < 0) {
+ pr_err("%s:ADM enable for port %d failed\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s ADM open failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ }
+ atomic_inc(&this_adm.copp_cnt[index]);
+
+ /* Set up routing for cached session */
+ for (i = find_first_bit(&this_adm.sessions[index], ASM_MAX_SESSION);
+ i < ASM_MAX_SESSION; i = find_next_bit(&this_adm.sessions[index],
+ ASM_MAX_SESSION, i + 1))
+ adm_cmd_map(port_id, i); /* Not thread safe */
+
+fail_cmd:
+ return ret;
+}
+
+int adm_open(int port_id, int path, int rate, int channel_mode, int topology)
+{
+ struct adm_copp_open_command open;
+ int ret = 0;
+ int index;
+
+ pr_debug("%s: port %d path:%d rate:%d mode:%d\n", __func__,
+ port_id, path, rate, channel_mode);
+
+ if (afe_validate_port(port_id) < 0) {
+ pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+ return -ENODEV;
+ }
+
+ index = afe_get_port_index(port_id);
+ pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
+
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+#ifdef CONFIG_MSM8X60_RTAC
+ rtac_set_adm_handle(this_adm.apr);
+#endif
+ }
+
+
+ /* Create a COPP if port id are not enabled */
+ if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
+
+ open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ open.hdr.pkt_size = sizeof(open);
+ open.hdr.src_svc = APR_SVC_ADM;
+ open.hdr.src_domain = APR_DOMAIN_APPS;
+ open.hdr.src_port = port_id;
+ open.hdr.dest_svc = APR_SVC_ADM;
+ open.hdr.dest_domain = APR_DOMAIN_ADSP;
+ open.hdr.dest_port = port_id;
+ open.hdr.token = port_id;
+ open.hdr.opcode = ADM_CMD_COPP_OPEN;
+
+ open.mode = path;
+ open.endpoint_id1 = port_id;
+ open.endpoint_id2 = 0xFFFF;
+
+ open.topology_id = get_adm_topology();
+ if (open.topology_id == 0)
+ open.topology_id = topology;
+
+ open.channel_config = channel_mode & 0x00FF;
+ open.rate = rate;
+
+ pr_debug("%s: channel_config=%d port_id=%d rate=%d\
+ topology_id=0x%X\n", __func__, open.channel_config,\
+ open.endpoint_id1, open.rate,\
+ open.topology_id);
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
+ if (ret < 0) {
+ pr_err("%s:ADM enable for port %d failed\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s ADM open failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ }
+ atomic_inc(&this_adm.copp_cnt[index]);
+ return 0;
+
+fail_cmd:
+
+ return ret;
+}
+
+int adm_matrix_map(int session_id, int path, int num_copps,
+ unsigned int *port_id, int copp_id)
+{
+ struct adm_routings_command route;
+ int ret = 0, i = 0;
+ /* Assumes port_ids have already been validated during adm_open */
+ int index = afe_get_port_index(copp_id);
+
+ pr_debug("%s: session 0x%x path:%d num_copps:%d port_id[0]:%d\n",
+ __func__, session_id, path, num_copps, port_id[0]);
+
+ route.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ route.hdr.pkt_size = sizeof(route);
+ route.hdr.src_svc = 0;
+ route.hdr.src_domain = APR_DOMAIN_APPS;
+ route.hdr.src_port = copp_id;
+ route.hdr.dest_svc = APR_SVC_ADM;
+ route.hdr.dest_domain = APR_DOMAIN_ADSP;
+ route.hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
+ route.hdr.token = copp_id;
+ route.hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS;
+ route.num_sessions = 1;
+ route.session[0].id = session_id;
+ route.session[0].num_copps = num_copps;
+
+ for (i = 0; i < num_copps; i++) {
+ int tmp;
+ tmp = afe_get_port_index(port_id[i]);
+
+ pr_debug("%s: port_id[%d]: %d, index: %d\n", __func__, i,
+ port_id[i], tmp);
+
+ route.session[0].copp_id[i] =
+ atomic_read(&this_adm.copp_id[tmp]);
+ }
+ if (num_copps % 2)
+ route.session[0].copp_id[i] = 0;
+
+ switch (path) {
+ case 0x1:
+ route.path = AUDIO_RX;
+ break;
+ case 0x2:
+ case 0x3:
+ route.path = AUDIO_TX;
+ break;
+ default:
+ pr_err("%s: Wrong path set[%d]\n", __func__, path);
+ break;
+ }
+ atomic_set(&this_adm.copp_stat[index], 0);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&route);
+ if (ret < 0) {
+ pr_err("%s: ADM routing for port %d failed\n",
+ __func__, port_id[0]);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: ADM cmd Route failed for port %d\n",
+ __func__, port_id[0]);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ for (i = 0; i < num_copps; i++)
+ send_adm_cal(port_id[i], path);
+
+#ifdef CONFIG_MSM8X60_RTAC
+ for (i = 0; i < num_copps; i++)
+ rtac_add_adm_device(port_id[i], session_id);
+#endif
+ return 0;
+
+fail_cmd:
+
+ return ret;
+}
+
+int adm_memory_map_regions(uint32_t *buf_add, uint32_t mempool_id,
+ uint32_t *bufsz, uint32_t bufcnt)
+{
+ struct adm_cmd_memory_map_regions *mmap_regions = NULL;
+ struct adm_memory_map_regions *mregions = NULL;
+ void *mmap_region_cmd = NULL;
+ void *payload = NULL;
+ int ret = 0;
+ int i = 0;
+ int cmd_size = 0;
+
+ pr_info("%s\n", __func__);
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+#ifdef CONFIG_MSM8X60_RTAC
+ rtac_set_adm_handle(this_adm.apr);
+#endif
+ }
+
+ cmd_size = sizeof(struct adm_cmd_memory_map_regions)
+ + sizeof(struct adm_memory_map_regions) * bufcnt;
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!mmap_region_cmd) {
+ pr_err("%s: allocate mmap_region_cmd failed\n", __func__);
+ return -ENOMEM;
+ }
+ mmap_regions = (struct adm_cmd_memory_map_regions *)mmap_region_cmd;
+ mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mmap_regions->hdr.pkt_size = cmd_size;
+ mmap_regions->hdr.src_port = 0;
+ mmap_regions->hdr.dest_port = 0;
+ mmap_regions->hdr.token = 0;
+ mmap_regions->hdr.opcode = ADM_CMD_MEMORY_MAP_REGIONS;
+ mmap_regions->mempool_id = mempool_id & 0x00ff;
+ mmap_regions->nregions = bufcnt & 0x00ff;
+ pr_debug("%s: map_regions->nregions = %d\n", __func__,
+ mmap_regions->nregions);
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct adm_cmd_memory_map_regions));
+ mregions = (struct adm_memory_map_regions *)payload;
+
+ for (i = 0; i < bufcnt; i++) {
+ mregions->phys = buf_add[i];
+ mregions->buf_size = bufsz[i];
+ ++mregions;
+ }
+
+ atomic_set(&this_adm.copp_stat[0], 0);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *) mmap_region_cmd);
+ if (ret < 0) {
+ pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
+ mmap_regions->hdr.opcode, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[0]), 5 * HZ);
+ if (!ret) {
+ pr_err("%s: timeout. waited for memory_map\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+fail_cmd:
+ kfree(mmap_region_cmd);
+ return ret;
+}
+
+int adm_memory_unmap_regions(uint32_t *buf_add, uint32_t *bufsz,
+ uint32_t bufcnt)
+{
+ struct adm_cmd_memory_unmap_regions *unmap_regions = NULL;
+ struct adm_memory_unmap_regions *mregions = NULL;
+ void *unmap_region_cmd = NULL;
+ void *payload = NULL;
+ int ret = 0;
+ int i = 0;
+ int cmd_size = 0;
+
+ pr_info("%s\n", __func__);
+
+ if (this_adm.apr == NULL) {
+ pr_err("%s APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ cmd_size = sizeof(struct adm_cmd_memory_unmap_regions)
+ + sizeof(struct adm_memory_unmap_regions) * bufcnt;
+
+ unmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!unmap_region_cmd) {
+ pr_err("%s: allocate unmap_region_cmd failed\n", __func__);
+ return -ENOMEM;
+ }
+ unmap_regions = (struct adm_cmd_memory_unmap_regions *)
+ unmap_region_cmd;
+ unmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ unmap_regions->hdr.pkt_size = cmd_size;
+ unmap_regions->hdr.src_port = 0;
+ unmap_regions->hdr.dest_port = 0;
+ unmap_regions->hdr.token = 0;
+ unmap_regions->hdr.opcode = ADM_CMD_MEMORY_UNMAP_REGIONS;
+ unmap_regions->nregions = bufcnt & 0x00ff;
+ unmap_regions->reserved = 0;
+ pr_debug("%s: unmap_regions->nregions = %d\n", __func__,
+ unmap_regions->nregions);
+ payload = ((u8 *) unmap_region_cmd +
+ sizeof(struct adm_cmd_memory_unmap_regions));
+ mregions = (struct adm_memory_unmap_regions *)payload;
+
+ for (i = 0; i < bufcnt; i++) {
+ mregions->phys = buf_add[i];
+ ++mregions;
+ }
+ atomic_set(&this_adm.copp_stat[0], 0);
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *) unmap_region_cmd);
+ if (ret < 0) {
+ pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
+ unmap_regions->hdr.opcode, ret);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[0]), 5 * HZ);
+ if (!ret) {
+ pr_err("%s: timeout. waited for memory_unmap\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+fail_cmd:
+ kfree(unmap_region_cmd);
+ return ret;
+}
+
+#ifdef CONFIG_MSM8X60_RTAC
+int adm_get_copp_id(int port_id)
+{
+ pr_debug("%s\n", __func__);
+
+ if (port_id < 0) {
+ pr_err("%s: invalid port_id = %d\n", __func__, port_id);
+ return -EINVAL;
+ }
+
+ return atomic_read(&this_adm.copp_id[port_id]);
+}
+#endif
+
+int adm_close(int port_id)
+{
+ struct apr_hdr close;
+
+ int ret = 0;
+ int index = afe_get_port_index(port_id);
+
+ pr_info("%s port_id=%d index %d\n", __func__, port_id, index);
+
+ if (!(atomic_read(&this_adm.copp_cnt[index]))) {
+ pr_err("%s: copp count for port[%d]is 0\n", __func__, port_id);
+
+ goto fail_cmd;
+ }
+ atomic_dec(&this_adm.copp_cnt[index]);
+ if (!(atomic_read(&this_adm.copp_cnt[index]))) {
+
+ close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ close.pkt_size = sizeof(close);
+ close.src_svc = APR_SVC_ADM;
+ close.src_domain = APR_DOMAIN_APPS;
+ close.src_port = port_id;
+ close.dest_svc = APR_SVC_ADM;
+ close.dest_domain = APR_DOMAIN_ADSP;
+ close.dest_port = atomic_read(&this_adm.copp_id[index]);
+ close.token = port_id;
+ close.opcode = ADM_CMD_COPP_CLOSE;
+
+ atomic_set(&this_adm.copp_id[index], RESET_COPP_ID);
+ atomic_set(&this_adm.copp_stat[index], 0);
+
+
+ pr_debug("%s:coppid %d portid=%d index=%d coppcnt=%d\n",
+ __func__,
+ atomic_read(&this_adm.copp_id[index]),
+ port_id, index,
+ atomic_read(&this_adm.copp_cnt[index]));
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close);
+ if (ret < 0) {
+ pr_err("%s ADM close failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: ADM cmd Route failed for port %d\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+#ifdef CONFIG_MSM8X60_RTAC
+ rtac_remove_adm_device(port_id);
+#endif
+ }
+
+fail_cmd:
+ return ret;
+}
+
+static int __init adm_init(void)
+{
+ int i = 0;
+ init_waitqueue_head(&this_adm.wait);
+ this_adm.apr = NULL;
+
+ for (i = 0; i < AFE_MAX_PORTS; i++) {
+ atomic_set(&this_adm.copp_id[i], RESET_COPP_ID);
+ atomic_set(&this_adm.copp_cnt[i], 0);
+ atomic_set(&this_adm.copp_stat[i], 0);
+ }
+ return 0;
+}
+
+device_initcall(adm_init);
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
new file mode 100644
index 0000000..500ceb5
--- /dev/null
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -0,0 +1,868 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <sound/apr_audio.h>
+#include <sound/q6afe.h>
+
+struct afe_ctl {
+ void *apr;
+ atomic_t state;
+ atomic_t status;
+ wait_queue_head_t wait;
+ struct task_struct *task;
+};
+
+static struct afe_ctl this_afe;
+
+#define TIMEOUT_MS 1000
+#define Q6AFE_MAX_VOLUME 0x3FFF
+
+#define SIZEOF_CFG_CMD(y) \
+ (sizeof(struct apr_hdr) + sizeof(u16) + (sizeof(struct y)))
+
+static int32_t afe_callback(struct apr_client_data *data, void *priv)
+{
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("q6afe: reset event = %d %d apr[%p]\n",
+ data->reset_event, data->reset_proc, this_afe.apr);
+ if (this_afe.apr) {
+ apr_reset(this_afe.apr);
+ atomic_set(&this_afe.state, 0);
+ this_afe.apr = NULL;
+ }
+ /* send info to user */
+ pr_debug("task_name = %s pid = %d\n",
+ this_afe.task->comm, this_afe.task->pid);
+ send_sig(SIGUSR1, this_afe.task, 0);
+ }
+ if (data->payload_size) {
+ uint32_t *payload;
+ payload = data->payload;
+ pr_debug("%s: cmd = 0x%x status = 0x%x\n", __func__,
+ payload[0], payload[1]);
+ /* payload[1] contains the error status for response */
+ if (payload[1] != 0) {
+ atomic_set(&this_afe.status, -1);
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, payload[0], payload[1]);
+ }
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ switch (payload[0]) {
+ case AFE_PORT_AUDIO_IF_CONFIG:
+ case AFE_PORT_CMD_STOP:
+ case AFE_PORT_CMD_START:
+ case AFE_PORT_CMD_LOOPBACK:
+ case AFE_PORT_CMD_SIDETONE_CTL:
+ case AFE_PORT_CMD_SET_PARAM:
+ case AFE_PSEUDOPORT_CMD_START:
+ case AFE_PSEUDOPORT_CMD_STOP:
+ atomic_set(&this_afe.state, 0);
+ wake_up(&this_afe.wait);
+ break;
+ default:
+ pr_err("Unknown cmd 0x%x\n",
+ payload[0]);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+int afe_validate_port(u16 port_id)
+{
+ int ret;
+
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ case PCM_RX:
+ case PCM_TX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ case HDMI_RX:
+ case RSVD_2:
+ case RSVD_3:
+ case DIGI_MIC_TX:
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ case VOICE_PLAYBACK_TX:
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ case INT_BT_SCO_RX:
+ case INT_BT_SCO_TX:
+ case INT_BT_A2DP_RX:
+ case INT_FM_RX:
+ case INT_FM_TX:
+ {
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int afe_get_port_index(u16 port_id)
+{
+ switch (port_id) {
+ case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX;
+ case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX;
+ case PCM_RX: return IDX_PCM_RX;
+ case PCM_TX: return IDX_PCM_TX;
+ case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX;
+ case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX;
+ case MI2S_RX: return IDX_MI2S_RX;
+ case MI2S_TX: return IDX_MI2S_TX;
+ case HDMI_RX: return IDX_HDMI_RX;
+ case RSVD_2: return IDX_RSVD_2;
+ case RSVD_3: return IDX_RSVD_3;
+ case DIGI_MIC_TX: return IDX_DIGI_MIC_TX;
+ case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX;
+ case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX;
+ case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX;
+ case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX;
+ case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX;
+ case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX;
+ case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX;
+ case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX;
+ case INT_FM_RX: return IDX_INT_FM_RX;
+ case INT_FM_TX: return IDX_INT_FM_TX;
+
+ default: return -EINVAL;
+ }
+}
+
+int afe_sizeof_cfg_cmd(u16 port_id)
+{
+ int ret_size;
+ switch (port_id) {
+ case PRIMARY_I2S_RX:
+ case PRIMARY_I2S_TX:
+ case SECONDARY_I2S_RX:
+ case SECONDARY_I2S_TX:
+ case MI2S_RX:
+ case MI2S_TX:
+ ret_size = SIZEOF_CFG_CMD(afe_port_mi2s_cfg);
+ break;
+ case HDMI_RX:
+ ret_size = SIZEOF_CFG_CMD(afe_port_hdmi_cfg);
+ break;
+ case SLIMBUS_0_RX:
+ case SLIMBUS_0_TX:
+ ret_size = SIZEOF_CFG_CMD(afe_port_slimbus_cfg);
+ break;
+ case PCM_RX:
+ case PCM_TX:
+ default:
+ ret_size = SIZEOF_CFG_CMD(afe_port_pcm_cfg);
+ break;
+ }
+ return ret_size;
+}
+
+int afe_port_start_nowait(u16 port_id, union afe_port_config *afe_config,
+ u32 rate) /* This function is no blocking */
+{
+ struct afe_port_start_command start;
+ struct afe_audioif_config_command config;
+ int ret;
+
+ if (!afe_config) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ pr_info("%s: %d %d\n", __func__, port_id, rate);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_info("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = afe_sizeof_cfg_cmd(port_id);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = 0;
+ config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG;
+
+ if (afe_validate_port(port_id) < 0) {
+
+ pr_err("%s: Failed : Invalid Port id = %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ config.port_id = port_id;
+ config.port = *afe_config;
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = 0;
+ start.hdr.opcode = AFE_PORT_CMD_START;
+ start.port_id = port_id;
+ start.gain = 0x2000;
+ start.sample_rate = rate;
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
+
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: AFE enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (this_afe.task != current)
+ this_afe.task = current;
+
+ pr_debug("task_name = %s pid = %d\n",
+ this_afe.task->comm, this_afe.task->pid);
+ return 0;
+
+fail_cmd:
+ return ret;
+}
+
+int afe_open(u16 port_id, union afe_port_config *afe_config, int rate)
+{
+ struct afe_port_start_command start;
+ struct afe_audioif_config_command config;
+ int ret = 0;
+
+ if (!afe_config) {
+ pr_err("%s: Error, no configuration data\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ pr_info("%s: %d %d\n", __func__, port_id, rate);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_info("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = afe_sizeof_cfg_cmd(port_id);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = 0;
+ config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG;
+
+ if (afe_validate_port(port_id) < 0) {
+
+ pr_err("%s: Failed : Invalid Port id = %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ config.port_id = port_id;
+ config.port = *afe_config;
+
+ atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: config cmd failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = 0;
+ start.hdr.opcode = AFE_PORT_CMD_START;
+ start.port_id = port_id;
+ start.gain = 0x2000;
+ start.sample_rate = rate;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ if (this_afe.task != current)
+ this_afe.task = current;
+
+ pr_debug("task_name = %s pid = %d\n",
+ this_afe.task->comm, this_afe.task->pid);
+ return 0;
+fail_cmd:
+ return ret;
+}
+
+int afe_loopback(u16 enable, u16 rx_port, u16 tx_port)
+{
+ struct afe_loopback_command lb_cmd;
+ int ret = 0;
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_info("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+ lb_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(20), APR_PKT_VER);
+ lb_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(lb_cmd) - APR_HDR_SIZE);
+ lb_cmd.hdr.src_port = 0;
+ lb_cmd.hdr.dest_port = 0;
+ lb_cmd.hdr.token = 0;
+ lb_cmd.hdr.opcode = AFE_PORT_CMD_LOOPBACK;
+ lb_cmd.tx_port_id = tx_port;
+ lb_cmd.rx_port_id = rx_port;
+ lb_cmd.mode = 0xFFFF;
+ lb_cmd.enable = (enable ? 1 : 0);
+ atomic_set(&this_afe.state, 1);
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &lb_cmd);
+ if (ret < 0) {
+ pr_err("%s: AFE loopback failed\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ }
+done:
+ return ret;
+}
+
+
+int afe_loopback_gain(u16 port_id, u16 volume)
+{
+ struct afe_port_cmd_set_param set_param;
+ int ret = 0;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE is not opened\n", __func__);
+ ret = -EPERM;
+ goto fail_cmd;
+ }
+
+ if (afe_validate_port(port_id) < 0) {
+
+ pr_err("%s: Failed : Invalid Port id = %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ /* RX ports numbers are even .TX ports numbers are odd. */
+ if (port_id % 2 == 0) {
+ pr_err("%s: Failed : afe loopback gain only for TX ports."
+ " port_id %d\n", __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ pr_debug("%s: %d %hX\n", __func__, port_id, volume);
+
+ set_param.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ set_param.hdr.pkt_size = sizeof(set_param);
+ set_param.hdr.src_port = 0;
+ set_param.hdr.dest_port = 0;
+ set_param.hdr.token = 0;
+ set_param.hdr.opcode = AFE_PORT_CMD_SET_PARAM;
+
+ set_param.port_id = port_id;
+ set_param.payload_size = sizeof(struct afe_param_payload);
+ set_param.payload_address = 0;
+
+ set_param.payload.module_id = AFE_MODULE_ID_PORT_INFO;
+ set_param.payload.param_id = AFE_PARAM_ID_LOOPBACK_GAIN;
+ set_param.payload.param_size = sizeof(struct afe_param_loopback_gain);
+ set_param.payload.reserved = 0;
+
+ set_param.payload.param.loopback_gain.gain = volume;
+ set_param.payload.param.loopback_gain.reserved = 0;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &set_param);
+ if (ret < 0) {
+ pr_err("%s: AFE param set failed for port %d\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (ret < 0) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return ret;
+}
+
+int afe_start_pseudo_port(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_start_command start;
+
+ pr_info("%s: port_id=%d\n", __func__, port_id);
+
+ if (this_afe.apr == NULL) {
+ this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+ 0xFFFFFFFF, &this_afe);
+ pr_info("%s: Register AFE\n", __func__);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Unable to register AFE\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ }
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = 0;
+ start.hdr.opcode = AFE_PSEUDOPORT_CMD_START;
+ start.port_id = port_id;
+ start.timing = 1;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed %d\n",
+ __func__, port_id, ret);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ return 0;
+}
+
+int afe_stop_pseudo_port(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_stop_command stop;
+
+ pr_info("%s: port_id=%d\n", __func__, port_id);
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE is already closed\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
+ if (ret < 0) {
+ pr_err("%s: AFE close failed %d\n", __func__, ret);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_afelb;
+static struct dentry *debugfs_afelb_gain;
+
+static int afe_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ pr_info("debug intf %s\n", (char *) file->private_data);
+ return 0;
+}
+
+static int afe_get_parameters(char *buf, long int *param1, int num_of_par)
+{
+ char *token;
+ int base, cnt;
+
+ token = strsep(&buf, " ");
+
+ for (cnt = 0; cnt < num_of_par; cnt++) {
+ if (token != NULL) {
+ if ((token[1] == 'x') || (token[1] == 'X'))
+ base = 16;
+ else
+ base = 10;
+
+ if (strict_strtoul(token, base, ¶m1[cnt]) != 0)
+ return -EINVAL;
+
+ token = strsep(&buf, " ");
+ } else
+ return -EINVAL;
+ }
+ return 0;
+}
+#define AFE_LOOPBACK_ON (1)
+#define AFE_LOOPBACK_OFF (0)
+static ssize_t afe_debug_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char *lb_str = filp->private_data;
+ char lbuf[32];
+ int rc;
+ unsigned long param[5];
+
+ if (cnt > sizeof(lbuf) - 1)
+ return -EINVAL;
+
+ rc = copy_from_user(lbuf, ubuf, cnt);
+ if (rc)
+ return -EFAULT;
+
+ lbuf[cnt] = '\0';
+
+ if (!strcmp(lb_str, "afe_loopback")) {
+ rc = afe_get_parameters(lbuf, param, 3);
+ if (!rc) {
+ pr_info("%s %lu %lu %lu\n", lb_str, param[0], param[1],
+ param[2]);
+
+ if ((param[0] != AFE_LOOPBACK_ON) && (param[0] !=
+ AFE_LOOPBACK_OFF)) {
+ pr_err("%s: Error, parameter 0 incorrect\n",
+ __func__);
+ rc = -EINVAL;
+ goto afe_error;
+ }
+ if ((afe_validate_port(param[1]) < 0) ||
+ (afe_validate_port(param[2])) < 0) {
+ pr_err("%s: Error, invalid afe port\n",
+ __func__);
+ }
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Error, AFE not opened\n", __func__);
+ rc = -EINVAL;
+ } else {
+ rc = afe_loopback(param[0], param[1], param[2]);
+ }
+ } else {
+ pr_err("%s: Error, invalid parameters\n", __func__);
+ rc = -EINVAL;
+ }
+
+ } else if (!strcmp(lb_str, "afe_loopback_gain")) {
+ rc = afe_get_parameters(lbuf, param, 2);
+ if (!rc) {
+ pr_info("%s %lu %lu\n", lb_str, param[0], param[1]);
+
+ if (afe_validate_port(param[0]) < 0) {
+ pr_err("%s: Error, invalid afe port\n",
+ __func__);
+ rc = -EINVAL;
+ goto afe_error;
+ }
+
+ if (param[1] < 0 || param[1] > 100) {
+ pr_err("%s: Error, volume shoud be 0 to 100"
+ " percentage param = %lu\n",
+ __func__, param[1]);
+ rc = -EINVAL;
+ goto afe_error;
+ }
+
+ param[1] = (Q6AFE_MAX_VOLUME * param[1]) / 100;
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: Error, AFE not opened\n", __func__);
+ rc = -EINVAL;
+ } else {
+ rc = afe_loopback_gain(param[0], param[1]);
+ }
+ } else {
+ pr_err("%s: Error, invalid parameters\n", __func__);
+ rc = -EINVAL;
+ }
+ }
+
+afe_error:
+ if (rc == 0)
+ rc = cnt;
+ else
+ pr_err("%s: rc = %d\n", __func__, rc);
+
+ return rc;
+}
+
+static const struct file_operations afe_debug_fops = {
+ .open = afe_debug_open,
+ .write = afe_debug_write
+};
+#endif
+int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain)
+{
+ struct afe_port_sidetone_command cmd_sidetone;
+ int ret = 0;
+
+ pr_info("%s: tx_port_id:%d rx_port_id:%d enable:%d gain:%d\n", __func__,
+ tx_port_id, rx_port_id, enable, gain);
+ cmd_sidetone.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cmd_sidetone.hdr.pkt_size = sizeof(cmd_sidetone);
+ cmd_sidetone.hdr.src_port = 0;
+ cmd_sidetone.hdr.dest_port = 0;
+ cmd_sidetone.hdr.token = 0;
+ cmd_sidetone.hdr.opcode = AFE_PORT_CMD_SIDETONE_CTL;
+ cmd_sidetone.tx_port_id = tx_port_id;
+ cmd_sidetone.rx_port_id = rx_port_id;
+ cmd_sidetone.gain = gain;
+ cmd_sidetone.enable = enable;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &cmd_sidetone);
+ if (ret < 0) {
+ pr_err("%s: AFE sidetone failed for tx_port:%d rx_port:%d\n",
+ __func__, tx_port_id, rx_port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (ret < 0) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return ret;
+}
+
+int afe_port_stop_nowait(int port_id)
+{
+ struct afe_port_stop_command stop;
+ int ret = 0;
+
+ if (this_afe.apr == NULL) {
+ pr_err("AFE is already closed\n");
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ pr_info("%s: port_id=%d\n", __func__, port_id);
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PORT_CMD_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
+
+ if (ret == -ENETRESET) {
+ pr_info("%s: Need to reset, calling APR deregister", __func__);
+ return apr_deregister(this_afe.apr);
+ } else if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: AFE close failed\n", __func__);
+ ret = -EINVAL;
+ }
+
+fail_cmd:
+ return ret;
+
+}
+
+int afe_close(int port_id)
+{
+ struct afe_port_stop_command stop;
+ int ret = 0;
+
+ if (this_afe.apr == NULL) {
+ pr_err("AFE is already closed\n");
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ pr_info("%s: port_id=%d\n", __func__, port_id);
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PORT_CMD_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
+
+ if (ret == -ENETRESET) {
+ pr_info("%s: Need to reset, calling APR deregister", __func__);
+ return apr_deregister(this_afe.apr);
+ }
+
+ if (ret < 0) {
+ pr_err("%s: AFE close failed\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+
+ ret = wait_event_timeout(this_afe.wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+fail_cmd:
+ return ret;
+}
+
+static int __init afe_init(void)
+{
+ init_waitqueue_head(&this_afe.wait);
+ atomic_set(&this_afe.state, 0);
+ atomic_set(&this_afe.status, 0);
+ this_afe.apr = NULL;
+#ifdef CONFIG_DEBUG_FS
+ debugfs_afelb = debugfs_create_file("afe_loopback",
+ S_IFREG | S_IWUGO, NULL, (void *) "afe_loopback",
+ &afe_debug_fops);
+
+ debugfs_afelb_gain = debugfs_create_file("afe_loopback_gain",
+ S_IFREG | S_IWUGO, NULL, (void *) "afe_loopback_gain",
+ &afe_debug_fops);
+
+
+#endif
+ return 0;
+}
+
+static void __exit afe_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ if (debugfs_afelb)
+ debugfs_remove(debugfs_afelb);
+ if (debugfs_afelb_gain)
+ debugfs_remove(debugfs_afelb_gain);
+#endif
+}
+
+device_initcall(afe_init);
+__exitcall(afe_exit);
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
new file mode 100644
index 0000000..f5f1c63
--- /dev/null
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -0,0 +1,2600 @@
+
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <linux/android_pmem.h>
+#include <linux/memory_alloc.h>
+#include <mach/memory.h>
+#include <mach/debug_mm.h>
+#include <mach/peripheral-loader.h>
+#include <mach/qdsp6v2/audio_acdb.h>
+#include <mach/qdsp6v2/rtac.h>
+#include <mach/msm_subsystem_map.h>
+#include <sound/apr_audio.h>
+#include <sound/q6asm.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#define TRUE 0x01
+#define FALSE 0x00
+#define READDONE_IDX_STATUS 0
+#define READDONE_IDX_BUFFER 1
+#define READDONE_IDX_SIZE 2
+#define READDONE_IDX_OFFSET 3
+#define READDONE_IDX_MSW_TS 4
+#define READDONE_IDX_LSW_TS 5
+#define READDONE_IDX_FLAGS 6
+#define READDONE_IDX_NUMFRAMES 7
+#define READDONE_IDX_ID 8
+
+static DEFINE_MUTEX(session_lock);
+
+/* session id: 0 reserved */
+static struct audio_client *session[SESSION_MAX+1];
+static int32_t q6asm_mmapcallback(struct apr_client_data *data, void *priv);
+static int32_t q6asm_callback(struct apr_client_data *data, void *priv);
+static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg);
+static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg);
+static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
+ uint32_t bufsz, uint32_t bufcnt);
+static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir,
+ uint32_t bufsz, uint32_t bufcnt);
+
+static void q6asm_reset_buf_state(struct audio_client *ac);
+
+struct asm_mmap {
+ atomic_t ref_cnt;
+ atomic_t cmd_state;
+ wait_queue_head_t cmd_wait;
+ void *apr;
+};
+
+static struct asm_mmap this_mmap;
+
+static int q6asm_session_alloc(struct audio_client *ac)
+{
+ int n;
+ mutex_lock(&session_lock);
+ for (n = 1; n <= SESSION_MAX; n++) {
+ if (!session[n]) {
+ session[n] = ac;
+ mutex_unlock(&session_lock);
+ return n;
+ }
+ }
+ mutex_unlock(&session_lock);
+ return -ENOMEM;
+}
+
+static void q6asm_session_free(struct audio_client *ac)
+{
+ pr_debug("%s: sessionid[%d]\n", __func__, ac->session);
+ mutex_lock(&session_lock);
+ session[ac->session] = 0;
+ mutex_unlock(&session_lock);
+ ac->session = 0;
+ return;
+}
+
+int q6asm_audio_client_buf_free(unsigned int dir,
+ struct audio_client *ac)
+{
+ struct audio_port_data *port;
+ int cnt = 0;
+ int rc = 0;
+ pr_debug("%s: Session id %d\n", __func__, ac->session);
+ mutex_lock(&ac->cmd_lock);
+ if (ac->io_mode == SYNC_IO_MODE) {
+ port = &ac->port[dir];
+ if (!port->buf) {
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+ }
+ cnt = port->max_buf_cnt - 1;
+
+ if (cnt >= 0) {
+ rc = q6asm_memory_unmap_regions(ac, dir,
+ port->buf[0].size,
+ port->max_buf_cnt);
+ if (rc < 0)
+ pr_err("%s CMD Memory_unmap_regions failed\n",
+ __func__);
+ }
+
+ while (cnt >= 0) {
+ if (port->buf[cnt].data) {
+ pr_debug("%s:data[%p]phys[%p][%p] cnt[%d]"
+ "mem_buffer[%p]\n",
+ __func__, (void *)port->buf[cnt].data,
+ (void *)port->buf[cnt].phys,
+ (void *)&port->buf[cnt].phys, cnt,
+ (void *)port->buf[cnt].mem_buffer);
+ if (IS_ERR((void *)port->buf[cnt].mem_buffer))
+ pr_err("%s:mem buffer invalid, error ="
+ "%ld\n", __func__,
+ PTR_ERR((void *)port->buf[cnt].mem_buffer));
+ else {
+ if (msm_subsystem_unmap_buffer(
+ port->buf[cnt].mem_buffer) < 0)
+ pr_err("%s: unmap buffer"
+ " failed\n", __func__);
+ }
+ free_contiguous_memory_by_paddr(
+ port->buf[cnt].phys);
+
+ port->buf[cnt].data = NULL;
+ port->buf[cnt].phys = 0;
+ --(port->max_buf_cnt);
+ }
+ --cnt;
+ }
+ kfree(port->buf);
+ port->buf = NULL;
+ }
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+}
+
+int q6asm_audio_client_buf_free_contiguous(unsigned int dir,
+ struct audio_client *ac)
+{
+ struct audio_port_data *port;
+ int cnt = 0;
+ int rc = 0;
+ pr_debug("%s: Session id %d\n", __func__, ac->session);
+ mutex_lock(&ac->cmd_lock);
+ port = &ac->port[dir];
+ if (!port->buf) {
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+ }
+ cnt = port->max_buf_cnt - 1;
+
+ if (cnt >= 0) {
+ rc = q6asm_memory_unmap(ac, port->buf[0].size, dir);
+ if (rc < 0)
+ pr_err("%s CMD Memory_unmap_regions failed\n",
+ __func__);
+ }
+
+ if (port->buf[0].data) {
+ pr_debug("%s:data[%p]phys[%p][%p] cnt[%d]\n",
+ __func__,
+ (void *)port->buf[0].data,
+ (void *)port->buf[0].phys,
+ (void *)&port->buf[0].phys, cnt);
+ dma_free_coherent(NULL,
+ port->buf[0].size * port->max_buf_cnt,
+ port->buf[0].data,
+ port->buf[0].phys);
+ }
+ while (cnt >= 0) {
+ port->buf[cnt].data = NULL;
+ port->buf[cnt].phys = 0;
+ cnt--;
+ }
+ port->max_buf_cnt = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+ mutex_unlock(&ac->cmd_lock);
+ return 0;
+}
+
+void q6asm_audio_client_free(struct audio_client *ac)
+{
+ int loopcnt;
+ struct audio_port_data *port;
+ if (!ac || !ac->session)
+ return;
+ pr_debug("%s: Session id %d\n", __func__, ac->session);
+ if (ac->io_mode == SYNC_IO_MODE) {
+ for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
+ port = &ac->port[loopcnt];
+ if (!port->buf)
+ continue;
+ pr_debug("%s:loopcnt = %d\n", __func__, loopcnt);
+ q6asm_audio_client_buf_free(loopcnt, ac);
+ }
+ }
+
+ apr_deregister(ac->apr);
+ q6asm_session_free(ac);
+
+ pr_debug("%s: APR De-Register\n", __func__);
+ if (atomic_read(&this_mmap.ref_cnt) <= 0) {
+ pr_err("%s: APR Common Port Already Closed\n", __func__);
+ goto done;
+ }
+
+ atomic_dec(&this_mmap.ref_cnt);
+ if (atomic_read(&this_mmap.ref_cnt) == 0) {
+ apr_deregister(this_mmap.apr);
+ pr_debug("%s:APR De-Register common port\n", __func__);
+ }
+done:
+ kfree(ac);
+ return;
+}
+
+int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode)
+{
+ if (ac == NULL) {
+ pr_err("%s APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) {
+ ac->io_mode = mode;
+ pr_debug("%s:Set Mode to %d\n", __func__, ac->io_mode);
+ return 0;
+ } else {
+ pr_err("%s:Not an valid IO Mode:%d\n", __func__, ac->io_mode);
+ return -EINVAL;
+ }
+}
+
+struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
+{
+ struct audio_client *ac;
+ int n;
+ int lcnt = 0;
+
+ ac = kzalloc(sizeof(struct audio_client), GFP_KERNEL);
+ if (!ac)
+ return NULL;
+ n = q6asm_session_alloc(ac);
+ if (n <= 0)
+ goto fail_session;
+ ac->session = n;
+ ac->cb = cb;
+ ac->priv = priv;
+ ac->io_mode = SYNC_IO_MODE;
+ ac->apr = apr_register("ADSP", "ASM", \
+ (apr_fn)q6asm_callback,\
+ ((ac->session) << 8 | 0x0001),\
+ ac);
+
+ if (ac->apr == NULL) {
+ pr_err("%s Registration with APR failed\n", __func__);
+ goto fail;
+ }
+#ifdef CONFIG_MSM8X60_RTAC
+ rtac_set_asm_handle(n, ac->apr);
+#endif
+ pr_debug("%s Registering the common port with APR\n", __func__);
+ if (atomic_read(&this_mmap.ref_cnt) == 0) {
+ this_mmap.apr = apr_register("ADSP", "ASM", \
+ (apr_fn)q6asm_mmapcallback,\
+ 0x0FFFFFFFF, &this_mmap);
+ if (this_mmap.apr == NULL) {
+ pr_debug("%s Unable to register \
+ APR ASM common port \n", __func__);
+ goto fail;
+ }
+ }
+
+ atomic_inc(&this_mmap.ref_cnt);
+ init_waitqueue_head(&ac->cmd_wait);
+ init_waitqueue_head(&ac->time_wait);
+ atomic_set(&ac->time_flag, 1);
+ mutex_init(&ac->cmd_lock);
+ for (lcnt = 0; lcnt <= OUT; lcnt++) {
+ mutex_init(&ac->port[lcnt].lock);
+ spin_lock_init(&ac->port[lcnt].dsp_lock);
+ }
+ atomic_set(&ac->cmd_state, 0);
+
+ pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+ return ac;
+fail:
+ q6asm_audio_client_free(ac);
+ return NULL;
+fail_session:
+ kfree(ac);
+ return NULL;
+}
+
+int q6asm_audio_client_buf_alloc(unsigned int dir,
+ struct audio_client *ac,
+ unsigned int bufsz,
+ unsigned int bufcnt)
+{
+ int cnt = 0;
+ int rc = 0;
+ struct audio_buffer *buf;
+
+ if (!(ac) || ((dir != IN) && (dir != OUT)))
+ return -EINVAL;
+
+ pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n", __func__, ac->session,
+ bufsz, bufcnt);
+
+ if (ac->session <= 0 || ac->session > 8)
+ goto fail;
+
+ if (ac->io_mode == SYNC_IO_MODE) {
+ if (ac->port[dir].buf) {
+ pr_debug("%s: buffer already allocated\n", __func__);
+ return 0;
+ }
+ mutex_lock(&ac->cmd_lock);
+ buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt),
+ GFP_KERNEL);
+
+ if (!buf) {
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ ac->port[dir].buf = buf;
+
+ while (cnt < bufcnt) {
+ if (bufsz > 0) {
+ if (!buf[cnt].data) {
+ unsigned int flags = 0;
+ buf[cnt].phys =
+ allocate_contiguous_ebi_nomap(bufsz,
+ SZ_4K);
+ if (!buf[cnt].phys) {
+ pr_err("%s:Buf alloc failed "
+ " size=%d\n", __func__,
+ bufsz);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+ flags = MSM_SUBSYSTEM_MAP_KADDR |
+ MSM_SUBSYSTEM_MAP_CACHED;
+ buf[cnt].mem_buffer =
+ msm_subsystem_map_buffer(buf[cnt].phys,
+ bufsz, flags, NULL, 0);
+ if (IS_ERR(
+ (void *)buf[cnt].mem_buffer)) {
+ pr_err("%s:map_buffer failed,"
+ "error = %ld\n",
+ __func__, PTR_ERR((void *)buf[cnt].mem_buffer));
+ goto fail;
+ }
+ buf[cnt].data =
+ buf[cnt].mem_buffer->vaddr;
+ if (!buf[cnt].data) {
+ pr_err("%s:invalid vaddr,"
+ " iomap failed\n", __func__);
+ goto fail;
+ }
+ buf[cnt].used = 1;
+ buf[cnt].size = bufsz;
+ buf[cnt].actual_size = bufsz;
+ pr_debug("%s data[%p]phys[%p][%p]"
+ "mem_buffer[%p]\n",
+ __func__,
+ (void *)buf[cnt].data,
+ (void *)buf[cnt].phys,
+ (void *)&buf[cnt].phys,
+ (void *)buf[cnt].mem_buffer);
+ cnt++;
+ }
+ }
+ }
+ ac->port[dir].max_buf_cnt = cnt;
+
+ mutex_unlock(&ac->cmd_lock);
+ rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt);
+ if (rc < 0) {
+ pr_err("%s:CMD Memory_map_regions failed\n", __func__);
+ goto fail;
+ }
+ }
+ return 0;
+fail:
+ q6asm_audio_client_buf_free(dir, ac);
+ return -EINVAL;
+}
+
+int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir,
+ struct audio_client *ac,
+ unsigned int bufsz,
+ unsigned int bufcnt)
+{
+ int cnt = 0;
+ int rc = 0;
+ struct audio_buffer *buf;
+
+ if (!(ac) || ((dir != IN) && (dir != OUT)))
+ return -EINVAL;
+
+ pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n",
+ __func__, ac->session,
+ bufsz, bufcnt);
+
+ if (ac->session <= 0 || ac->session > 8)
+ goto fail;
+
+ if (ac->port[dir].buf) {
+ pr_debug("%s: buffer already allocated\n", __func__);
+ return 0;
+ }
+ mutex_lock(&ac->cmd_lock);
+ buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt),
+ GFP_KERNEL);
+
+ if (!buf) {
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+
+ ac->port[dir].buf = buf;
+
+ buf[0].data = dma_alloc_coherent(NULL, bufsz * bufcnt,
+ &buf[0].phys, GFP_KERNEL);
+ buf[0].used = dir ^ 1;
+ buf[0].size = bufsz;
+ buf[0].actual_size = bufsz;
+ cnt = 1;
+ while (cnt < bufcnt) {
+ if (bufsz > 0) {
+ buf[cnt].data = buf[0].data + (cnt * bufsz);
+ buf[cnt].phys = buf[0].phys + (cnt * bufsz);
+ if (!buf[cnt].data) {
+ pr_err("%s Buf alloc failed\n",
+ __func__);
+ mutex_unlock(&ac->cmd_lock);
+ goto fail;
+ }
+ buf[cnt].used = dir ^ 1;
+ buf[cnt].size = bufsz;
+ buf[cnt].actual_size = bufsz;
+ pr_debug("%s data[%p]phys[%p][%p]\n", __func__,
+ (void *)buf[cnt].data,
+ (void *)buf[cnt].phys,
+ (void *)&buf[cnt].phys);
+ }
+ cnt++;
+ }
+ ac->port[dir].max_buf_cnt = cnt;
+ mutex_unlock(&ac->cmd_lock);
+ rc = q6asm_memory_map(ac, buf[0].phys, dir, bufsz, cnt);
+ if (rc < 0) {
+ pr_err("%s:CMD Memory_map_regions failed\n", __func__);
+ goto fail;
+ }
+ return 0;
+fail:
+ q6asm_audio_client_buf_free_contiguous(dir, ac);
+ return -EINVAL;
+}
+
+static int32_t q6asm_mmapcallback(struct apr_client_data *data, void *priv)
+{
+ uint32_t token;
+ uint32_t *payload = data->payload;
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("%s: Reset event is received: %d %d apr[%p]\n",
+ __func__,
+ data->reset_event,
+ data->reset_proc,
+ this_mmap.apr);
+ apr_reset(this_mmap.apr);
+ this_mmap.apr = NULL;
+ atomic_set(&this_mmap.cmd_state, 0);
+ return 0;
+ }
+
+ pr_debug("%s:ptr0[0x%x]ptr1[0x%x]opcode[0x%x]"
+ "token[0x%x]payload_s[%d] src[%d] dest[%d]\n", __func__,
+ payload[0], payload[1], data->opcode, data->token,
+ data->payload_size, data->src_port, data->dest_port);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ token = data->token;
+ switch (payload[0]) {
+ case ASM_SESSION_CMD_MEMORY_MAP:
+ case ASM_SESSION_CMD_MEMORY_UNMAP:
+ case ASM_SESSION_CMD_MEMORY_MAP_REGIONS:
+ case ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS:
+ pr_debug("%s:command[0x%x]success [0x%x]\n",
+ __func__, payload[0], payload[1]);
+ if (atomic_read(&this_mmap.cmd_state)) {
+ atomic_set(&this_mmap.cmd_state, 0);
+ wake_up(&this_mmap.cmd_wait);
+ }
+ break;
+ default:
+ pr_debug("%s:command[0x%x] not expecting rsp\n",
+ __func__, payload[0]);
+ break;
+ }
+ }
+ return 0;
+}
+
+
+static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
+{
+ int i = 0;
+ struct audio_client *ac = (struct audio_client *)priv;
+ uint32_t token;
+ unsigned long dsp_flags;
+ uint32_t *payload;
+
+
+ if ((ac == NULL) || (data == NULL)) {
+ pr_err("ac or priv NULL\n");
+ return -EINVAL;
+ }
+ if (ac->session <= 0 || ac->session > 8) {
+ pr_err("%s:Session ID is invalid, session = %d\n", __func__,
+ ac->session);
+ return -EINVAL;
+ }
+
+ payload = data->payload;
+
+ if (data->opcode == RESET_EVENTS) {
+ pr_debug("q6asm_callback: Reset event is received: %d %d apr[%p]\n",
+ data->reset_event, data->reset_proc, ac->apr);
+ apr_reset(ac->apr);
+ return 0;
+ }
+
+ pr_debug("%s: session[%d]opcode[0x%x] \
+ token[0x%x]payload_s[%d] src[%d] dest[%d]\n", __func__,
+ ac->session, data->opcode,
+ data->token, data->payload_size, data->src_port,
+ data->dest_port);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ token = data->token;
+ switch (payload[0]) {
+ case ASM_STREAM_CMD_SET_PP_PARAMS:
+#ifdef CONFIG_MSM8X60_RTAC
+ if (rtac_make_asm_callback(ac->session, payload,
+ data->payload_size))
+ break;
+#endif
+ case ASM_SESSION_CMD_PAUSE:
+ case ASM_DATA_CMD_EOS:
+ case ASM_STREAM_CMD_CLOSE:
+ case ASM_STREAM_CMD_FLUSH:
+ case ASM_SESSION_CMD_RUN:
+ case ASM_SESSION_CMD_REGISTER_FOR_TX_OVERFLOW_EVENTS:
+ case ASM_STREAM_CMD_FLUSH_READBUFS:
+ pr_debug("%s:Payload = [0x%x]\n", __func__, payload[0]);
+ if (token != ac->session) {
+ pr_err("%s:Invalid session[%d] rxed expected[%d]",
+ __func__, token, ac->session);
+ return -EINVAL;
+ }
+ case ASM_STREAM_CMD_OPEN_READ:
+ case ASM_STREAM_CMD_OPEN_WRITE:
+ case ASM_STREAM_CMD_OPEN_READWRITE:
+ case ASM_DATA_CMD_MEDIA_FORMAT_UPDATE:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ if (atomic_read(&ac->cmd_state)) {
+ atomic_set(&ac->cmd_state, 0);
+ wake_up(&ac->cmd_wait);
+ }
+ if (ac->cb)
+ ac->cb(data->opcode, data->token,
+ (uint32_t *)data->payload, ac->priv);
+ break;
+ default:
+ pr_debug("%s:command[0x%x] not expecting rsp\n",
+ __func__, payload[0]);
+ break;
+ }
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE:{
+ struct audio_port_data *port = &ac->port[IN];
+ pr_debug("%s: Rxed opcode[0x%x] status[0x%x] token[%d]",
+ __func__, payload[0], payload[1],
+ data->token);
+ if (ac->io_mode == SYNC_IO_MODE) {
+ if (port->buf == NULL) {
+ pr_err("%s: Unexpected Write Done\n",
+ __func__);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ if (port->buf[data->token].phys !=
+ payload[0]) {
+ pr_err("Buf expected[%p]rxed[%p]\n",\
+ (void *)port->buf[data->token].phys,\
+ (void *)payload[0]);
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ return -EINVAL;
+ }
+ token = data->token;
+ port->buf[token].used = 1;
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+ for (i = 0; i < port->max_buf_cnt; i++)
+ pr_debug("%d ", port->buf[i].used);
+
+ }
+ break;
+ }
+#ifdef CONFIG_MSM8X60_RTAC
+ case ASM_STREAM_CMDRSP_GET_PP_PARAMS:
+ rtac_make_asm_callback(ac->session, payload,
+ data->payload_size);
+ break;
+#endif
+ case ASM_DATA_EVENT_READ_DONE:{
+
+ struct audio_port_data *port = &ac->port[OUT];
+
+ pr_debug("%s:R-D: status=%d buff_add=%x act_size=%d offset=%d\n",
+ __func__, payload[READDONE_IDX_STATUS],
+ payload[READDONE_IDX_BUFFER],
+ payload[READDONE_IDX_SIZE],
+ payload[READDONE_IDX_OFFSET]);
+ pr_debug("%s:R-D:msw_ts=%d lsw_ts=%d flags=%d id=%d num=%d\n",
+ __func__, payload[READDONE_IDX_MSW_TS],
+ payload[READDONE_IDX_LSW_TS],
+ payload[READDONE_IDX_FLAGS],
+ payload[READDONE_IDX_ID],
+ payload[READDONE_IDX_NUMFRAMES]);
+
+ if (ac->io_mode == SYNC_IO_MODE) {
+ if (port->buf == NULL) {
+ pr_err("%s: Unexpected Write Done\n", __func__);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ token = data->token;
+ port->buf[token].used = 0;
+ if (port->buf[token].phys !=
+ payload[READDONE_IDX_BUFFER]) {
+ pr_err("Buf expected[%p]rxed[%p]\n",\
+ (void *)port->buf[token].phys,\
+ (void *)payload[READDONE_IDX_BUFFER]);
+ spin_unlock_irqrestore(&port->dsp_lock,
+ dsp_flags);
+ break;
+ }
+ port->buf[token].actual_size =
+ payload[READDONE_IDX_SIZE];
+ spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+ }
+ break;
+ }
+ case ASM_DATA_EVENT_EOS:
+ case ASM_DATA_CMDRSP_EOS:
+ pr_debug("%s:EOS ACK received: rxed opcode[0x%x]\n",
+ __func__, data->opcode);
+ break;
+ case ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM:
+ break;
+ case ASM_SESSION_EVENT_TX_OVERFLOW:
+ pr_err("ASM_SESSION_EVENT_TX_OVERFLOW\n");
+ break;
+ case ASM_SESSION_CMDRSP_GET_SESSION_TIME:
+ pr_debug("%s: ASM_SESSION_CMDRSP_GET_SESSION_TIME, "
+ "payload[0] = %d, payload[1] = %d, "
+ "payload[2] = %d\n", __func__,
+ payload[0], payload[1], payload[2]);
+ ac->time_stamp = (uint64_t)(((uint64_t)payload[1] << 32) |
+ payload[2]);
+ if (atomic_read(&ac->time_flag)) {
+ atomic_set(&ac->time_flag, 0);
+ wake_up(&ac->time_wait);
+ }
+ break;
+ case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+ pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, "
+ "payload[0] = %d, payload[1] = %d, "
+ "payload[2] = %d, payload[3] = %d\n", __func__,
+ payload[0], payload[1], payload[2],
+ payload[3]);
+ break;
+ }
+ if (ac->cb)
+ ac->cb(data->opcode, data->token,
+ data->payload, ac->priv);
+
+ return 0;
+}
+
+void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, uint32_t *size,
+ uint32_t *index)
+{
+ void *data;
+ unsigned char idx;
+ struct audio_port_data *port;
+
+ if (!ac || ((dir != IN) && (dir != OUT)))
+ return NULL;
+
+ if (ac->io_mode == SYNC_IO_MODE) {
+ port = &ac->port[dir];
+
+ mutex_lock(&port->lock);
+ idx = port->cpu_buf;
+ if (port->buf == NULL) {
+ pr_debug("%s:Buffer pointer null\n", __func__);
+ return NULL;
+ }
+ /* dir 0: used = 0 means buf in use
+ dir 1: used = 1 means buf in use */
+ if (port->buf[idx].used == dir) {
+ /* To make it more robust, we could loop and get the
+ next avail buf, its risky though */
+ pr_debug("%s:Next buf idx[0x%x] not available,\
+ dir[%d]\n", __func__, idx, dir);
+ mutex_unlock(&port->lock);
+ return NULL;
+ }
+ *size = port->buf[idx].actual_size;
+ *index = port->cpu_buf;
+ data = port->buf[idx].data;
+ pr_debug("%s:session[%d]index[%d] data[%p]size[%d]\n",
+ __func__,
+ ac->session,
+ port->cpu_buf,
+ data, *size);
+ /* By default increase the cpu_buf cnt
+ user accesses this function,increase cpu
+ buf(to avoid another api)*/
+ port->buf[idx].used = dir;
+ port->cpu_buf = ((port->cpu_buf + 1) & (port->max_buf_cnt - 1));
+ mutex_unlock(&port->lock);
+ return data;
+ }
+ return NULL;
+}
+
+int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac)
+{
+ int ret = -1;
+ struct audio_port_data *port;
+ uint32_t idx;
+
+ if (!ac || (dir != OUT))
+ return ret;
+
+ if (ac->io_mode == SYNC_IO_MODE) {
+ port = &ac->port[dir];
+
+ mutex_lock(&port->lock);
+ idx = port->dsp_buf;
+
+ if (port->buf[idx].used == (dir ^ 1)) {
+ /* To make it more robust, we could loop and get the
+ next avail buf, its risky though */
+ pr_err("Next buf idx[0x%x] not available, dir[%d]\n",
+ idx, dir);
+ mutex_unlock(&port->lock);
+ return ret;
+ }
+ pr_debug("%s: session[%d]dsp_buf=%d cpu_buf=%d\n", __func__,
+ ac->session, port->dsp_buf, port->cpu_buf);
+ ret = ((port->dsp_buf != port->cpu_buf) ? 0 : -1);
+ mutex_unlock(&port->lock);
+ }
+ return ret;
+}
+
+static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg)
+{
+ pr_debug("%s:session=%d pkt size=%d cmd_flg=%d\n", __func__, pkt_size,
+ cmd_flg, ac->session);
+ mutex_lock(&ac->cmd_lock);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(sizeof(struct apr_hdr)),\
+ APR_PKT_VER);
+ hdr->src_svc = ((struct apr_svc *)ac->apr)->id;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_ASM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = ((ac->session << 8) & 0xFF00) | 0x01;
+ hdr->dest_port = ((ac->session << 8) & 0xFF00) | 0x01;
+ if (cmd_flg) {
+ hdr->token = ac->session;
+ atomic_set(&ac->cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ mutex_unlock(&ac->cmd_lock);
+ return;
+}
+
+static void q6asm_add_mmaphdr(struct apr_hdr *hdr, uint32_t pkt_size,
+ uint32_t cmd_flg)
+{
+ pr_debug("%s:pkt size=%d cmd_flg=%d\n", __func__, pkt_size, cmd_flg);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr->src_port = 0;
+ hdr->dest_port = 0;
+ if (cmd_flg) {
+ hdr->token = 0;
+ atomic_set(&this_mmap.cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+int q6asm_open_read(struct audio_client *ac,
+ uint32_t format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_read open;
+
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s:session[%d]", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ;
+ /* Stream prio : High, provide meta info with encoded frames */
+ open.src_endpoint = ASM_END_POINT_DEVICE_MATRIX;
+
+ open.pre_proc_top = get_asm_topology();
+ if (open.pre_proc_top == 0)
+ open.pre_proc_top = DEFAULT_POPP_TOPOLOGY;
+
+ switch (format) {
+ case FORMAT_LINEAR_PCM:
+ open.uMode = STREAM_PRIORITY_HIGH;
+ open.format = LINEAR_PCM;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = MPEG4_AAC;
+ break;
+ case FORMAT_V13K:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = EVRC_FS;
+ break;
+ case FORMAT_AMRNB:
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_HIGH;
+ open.format = AMRNB_FS;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", format);
+ goto fail_cmd;
+ }
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", \
+ open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for OPEN_WRITE rc[%d]\n", __func__,
+ rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_open_write(struct audio_client *ac, uint32_t format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_write open;
+
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d] wr_format[0x%x]", __func__, ac->session,
+ format);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE;
+ open.uMode = STREAM_PRIORITY_HIGH;
+ /* source endpoint : matrix */
+ open.sink_endpoint = ASM_END_POINT_DEVICE_MATRIX;
+ open.stream_handle = 0x00;
+
+ open.post_proc_top = get_asm_topology();
+ if (open.post_proc_top == 0)
+ open.post_proc_top = DEFAULT_POPP_TOPOLOGY;
+
+ switch (format) {
+ case FORMAT_LINEAR_PCM:
+ open.format = LINEAR_PCM;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.format = MPEG4_AAC;
+ break;
+ case FORMAT_WMA_V9:
+ open.format = WMA_V9;
+ break;
+ case FORMAT_WMA_V10PRO:
+ open.format = WMA_V10PRO;
+ break;
+ default:
+ pr_err("%s: Invalid format[%d]\n", __func__, format);
+ goto fail_cmd;
+ }
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("%s: open failed op[0x%x]rc[%d]\n", \
+ __func__, open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout. waited for OPEN_WRITE rc[%d]\n", __func__,
+ rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_open_read_write(struct audio_client *ac,
+ uint32_t rd_format,
+ uint32_t wr_format)
+{
+ int rc = 0x00;
+ struct asm_stream_cmd_open_read_write open;
+
+ if ((ac == NULL) || (ac->apr == NULL)) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]", __func__, ac->session);
+ pr_debug("wr_format[0x%x]rd_format[0x%x]",
+ wr_format, rd_format);
+
+ q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+ open.hdr.opcode = ASM_STREAM_CMD_OPEN_READWRITE;
+
+ open.uMode = BUFFER_META_ENABLE | STREAM_PRIORITY_NORMAL |
+ SR_CM_NOTIFY_ENABLE;
+ /* source endpoint : matrix */
+ open.post_proc_top = get_asm_topology();
+ if (open.post_proc_top == 0)
+ open.post_proc_top = DEFAULT_POPP_TOPOLOGY;
+
+ switch (wr_format) {
+ case FORMAT_LINEAR_PCM:
+ open.write_format = LINEAR_PCM;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.write_format = MPEG4_AAC;
+ break;
+ case FORMAT_WMA_V9:
+ open.write_format = WMA_V9;
+ break;
+ case FORMAT_WMA_V10PRO:
+ open.write_format = WMA_V10PRO;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", wr_format);
+ goto fail_cmd;
+ }
+
+ switch (rd_format) {
+ case FORMAT_LINEAR_PCM:
+ open.read_format = LINEAR_PCM;
+ break;
+ case FORMAT_MPEG4_AAC:
+ open.read_format = MPEG4_AAC;
+ break;
+ case FORMAT_V13K:
+ open.read_format = V13K_FS;
+ break;
+ case FORMAT_EVRC:
+ open.read_format = EVRC_FS;
+ break;
+ case FORMAT_AMRNB:
+ open.read_format = AMRNB_FS;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", rd_format);
+ goto fail_cmd;
+ }
+ pr_debug("%s:rdformat[0x%x]wrformat[0x%x]\n", __func__,
+ open.read_format, open.write_format);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+ if (rc < 0) {
+ pr_err("open failed op[0x%x]rc[%d]\n", \
+ open.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for OPEN_WRITE rc[%d]\n", rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_run(struct audio_client *ac, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts)
+{
+ struct asm_stream_cmd_run run;
+ int rc;
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s session[%d]", __func__, ac->session);
+ q6asm_add_hdr(ac, &run.hdr, sizeof(run), TRUE);
+
+ run.hdr.opcode = ASM_SESSION_CMD_RUN;
+ run.flags = flags;
+ run.msw_ts = msw_ts;
+ run.lsw_ts = lsw_ts;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &run);
+ if (rc < 0) {
+ pr_err("Commmand run failed[%d]", rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for run success rc[%d]", rc);
+ goto fail_cmd;
+ }
+
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
+ uint32_t msw_ts, uint32_t lsw_ts)
+{
+ struct asm_stream_cmd_run run;
+ int rc;
+ if (!ac || ac->apr == NULL) {
+ pr_err("%s:APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ pr_debug("session[%d]", ac->session);
+ q6asm_add_hdr_async(ac, &run.hdr, sizeof(run), TRUE);
+
+ run.hdr.opcode = ASM_SESSION_CMD_RUN;
+ run.flags = flags;
+ run.msw_ts = msw_ts;
+ run.lsw_ts = lsw_ts;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &run);
+ if (rc < 0) {
+ pr_err("%s:Commmand run failed[%d]", __func__, rc);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+int q6asm_enc_cfg_blk_aac(struct audio_client *ac,
+ uint32_t frames_per_buf,
+ uint32_t sample_rate, uint32_t channels,
+ uint32_t bit_rate, uint32_t mode, uint32_t format)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]frames[%d]SR[%d]ch[%d]bitrate[%d]mode[%d]"
+ "format[%d]", __func__, ac->session, frames_per_buf,
+ sample_rate, channels, bit_rate, mode, format);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+ enc_cfg.enc_blk.frames_per_buf = frames_per_buf;
+ enc_cfg.enc_blk.format_id = MPEG4_AAC;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_aac_read_cfg);
+ enc_cfg.enc_blk.cfg.aac.bitrate = bit_rate;
+ enc_cfg.enc_blk.cfg.aac.enc_mode = mode;
+ enc_cfg.enc_blk.cfg.aac.format = format;
+ enc_cfg.enc_blk.cfg.aac.ch_cfg = channels;
+ enc_cfg.enc_blk.cfg.aac.sample_rate = sample_rate;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for FORMAT_UPDATE\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+
+ int rc = 0;
+
+ pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__,
+ ac->session, rate, channels);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+ enc_cfg.enc_blk.frames_per_buf = 1;
+ enc_cfg.enc_blk.format_id = LINEAR_PCM;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_pcm_cfg);
+ enc_cfg.enc_blk.cfg.pcm.ch_cfg = channels;
+ enc_cfg.enc_blk.cfg.pcm.bits_per_sample = 16;
+ enc_cfg.enc_blk.cfg.pcm.sample_rate = rate;
+ enc_cfg.enc_blk.cfg.pcm.is_signed = 1;
+ enc_cfg.enc_blk.cfg.pcm.interleaved = 1;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd open failed\n");
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout opcode[0x%x] ", enc_cfg.hdr.opcode);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enable_sbrps(struct audio_client *ac,
+ uint32_t sbr_ps_enable)
+{
+ struct asm_stream_cmd_encdec_sbr sbrps;
+
+ int rc = 0;
+
+ pr_debug("%s: Session %d\n", __func__, ac->session);
+
+ q6asm_add_hdr(ac, &sbrps.hdr, sizeof(sbrps), TRUE);
+
+ sbrps.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+ sbrps.param_id = ASM_ENABLE_SBR_PS;
+ sbrps.param_size = sizeof(struct asm_sbr_ps);
+ sbrps.sbr_ps.enable = sbr_ps_enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &sbrps);
+ if (rc < 0) {
+ pr_err("Command opcode[0x%x]paramid[0x%x] failed\n",
+ ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+ ASM_ENABLE_SBR_PS);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout opcode[0x%x] ", sbrps.hdr.opcode);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t min_rate, uint16_t max_rate,
+ uint16_t reduced_rate_level, uint16_t rate_modulation_cmd)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] \
+ reduced_rate_level[0x%4x]rate_modulation_cmd[0x%4x]", __func__,
+ ac->session, frames_per_buf, min_rate, max_rate,
+ reduced_rate_level, rate_modulation_cmd);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+
+ enc_cfg.enc_blk.frames_per_buf = frames_per_buf;
+ enc_cfg.enc_blk.format_id = V13K_FS;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_qcelp13_read_cfg);
+ enc_cfg.enc_blk.cfg.qcelp13.min_rate = min_rate;
+ enc_cfg.enc_blk.cfg.qcelp13.max_rate = max_rate;
+ enc_cfg.enc_blk.cfg.qcelp13.reduced_rate_level = reduced_rate_level;
+ enc_cfg.enc_blk.cfg.qcelp13.rate_modulation_cmd = rate_modulation_cmd;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for FORMAT_UPDATE\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t min_rate, uint16_t max_rate,
+ uint16_t rate_modulation_cmd)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] \
+ rate_modulation_cmd[0x%4x]", __func__, ac->session,
+ frames_per_buf, min_rate, max_rate, rate_modulation_cmd);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+
+ enc_cfg.enc_blk.frames_per_buf = frames_per_buf;
+ enc_cfg.enc_blk.format_id = EVRC_FS;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_evrc_read_cfg);
+ enc_cfg.enc_blk.cfg.evrc.min_rate = min_rate;
+ enc_cfg.enc_blk.cfg.evrc.max_rate = max_rate;
+ enc_cfg.enc_blk.cfg.evrc.rate_modulation_cmd = rate_modulation_cmd;
+ enc_cfg.enc_blk.cfg.evrc.reserved = 0;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for FORMAT_UPDATE\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf,
+ uint16_t band_mode, uint16_t dtx_enable)
+{
+ struct asm_stream_cmd_encdec_cfg_blk enc_cfg;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]",
+ __func__, ac->session, frames_per_buf, band_mode, dtx_enable);
+
+ q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+
+ enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+
+ enc_cfg.param_id = ASM_ENCDEC_CFG_BLK_ID;
+ enc_cfg.param_size = sizeof(struct asm_encode_cfg_blk);
+
+ enc_cfg.enc_blk.frames_per_buf = frames_per_buf;
+ enc_cfg.enc_blk.format_id = AMRNB_FS;
+ enc_cfg.enc_blk.cfg_size = sizeof(struct asm_amrnb_read_cfg);
+ enc_cfg.enc_blk.cfg.amrnb.mode = band_mode;
+ enc_cfg.enc_blk.cfg.amrnb.dtx_mode = dtx_enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+ if (rc < 0) {
+ pr_err("Comamnd %d failed\n", ASM_STREAM_CMD_SET_ENCDEC_PARAM);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for FORMAT_UPDATE\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_media_format_block_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct asm_stream_media_format_update fmt;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate,
+ channels);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = LINEAR_PCM;
+ fmt.cfg_size = sizeof(struct asm_pcm_cfg);
+ fmt.write_cfg.pcm_cfg.ch_cfg = channels;
+ fmt.write_cfg.pcm_cfg.bits_per_sample = 16;
+ fmt.write_cfg.pcm_cfg.sample_rate = rate;
+ fmt.write_cfg.pcm_cfg.is_signed = 1;
+ fmt.write_cfg.pcm_cfg.interleaved = 1;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_media_format_block_aac(struct audio_client *ac,
+ struct asm_aac_cfg *cfg)
+{
+ struct asm_stream_media_format_update fmt;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]rate[%d]ch[%d]\n", __func__, ac->session,
+ cfg->sample_rate, cfg->ch_cfg);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = MPEG4_AAC;
+ fmt.cfg_size = sizeof(struct asm_aac_cfg);
+ fmt.write_cfg.aac_cfg.format = cfg->format;
+ fmt.write_cfg.aac_cfg.aot = cfg->aot;
+ fmt.write_cfg.aac_cfg.ep_config = cfg->ep_config;
+ fmt.write_cfg.aac_cfg.section_data_resilience =
+ cfg->section_data_resilience;
+ fmt.write_cfg.aac_cfg.scalefactor_data_resilience =
+ cfg->scalefactor_data_resilience;
+ fmt.write_cfg.aac_cfg.spectral_data_resilience =
+ cfg->spectral_data_resilience;
+ fmt.write_cfg.aac_cfg.ch_cfg = cfg->ch_cfg;
+ fmt.write_cfg.aac_cfg.sample_rate = cfg->sample_rate;
+ pr_info("%s:format=%x cfg_size=%d aac-cfg=%x aot=%d ch=%d sr=%d\n",
+ __func__, fmt.format, fmt.cfg_size,
+ fmt.write_cfg.aac_cfg.format,
+ fmt.write_cfg.aac_cfg.aot,
+ fmt.write_cfg.aac_cfg.ch_cfg,
+ fmt.write_cfg.aac_cfg.sample_rate);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_media_format_block_wma(struct audio_client *ac,
+ void *cfg)
+{
+ struct asm_stream_media_format_update fmt;
+ struct asm_wma_cfg *wma_cfg = (struct asm_wma_cfg *)cfg;
+ int rc = 0;
+
+ pr_debug("session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d],\
+ balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x]\n",
+ ac->session, wma_cfg->format_tag, wma_cfg->sample_rate,
+ wma_cfg->ch_cfg, wma_cfg->avg_bytes_per_sec,
+ wma_cfg->block_align, wma_cfg->valid_bits_per_sample,
+ wma_cfg->ch_mask, wma_cfg->encode_opt);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = WMA_V9;
+ fmt.cfg_size = sizeof(struct asm_wma_cfg);
+ fmt.write_cfg.wma_cfg.format_tag = wma_cfg->format_tag;
+ fmt.write_cfg.wma_cfg.ch_cfg = wma_cfg->ch_cfg;
+ fmt.write_cfg.wma_cfg.sample_rate = wma_cfg->sample_rate;
+ fmt.write_cfg.wma_cfg.avg_bytes_per_sec = wma_cfg->avg_bytes_per_sec;
+ fmt.write_cfg.wma_cfg.block_align = wma_cfg->block_align;
+ fmt.write_cfg.wma_cfg.valid_bits_per_sample =
+ wma_cfg->valid_bits_per_sample;
+ fmt.write_cfg.wma_cfg.ch_mask = wma_cfg->ch_mask;
+ fmt.write_cfg.wma_cfg.encode_opt = wma_cfg->encode_opt;
+ fmt.write_cfg.wma_cfg.adv_encode_opt = 0;
+ fmt.write_cfg.wma_cfg.adv_encode_opt2 = 0;
+ fmt.write_cfg.wma_cfg.drc_peak_ref = 0;
+ fmt.write_cfg.wma_cfg.drc_peak_target = 0;
+ fmt.write_cfg.wma_cfg.drc_ave_ref = 0;
+ fmt.write_cfg.wma_cfg.drc_ave_target = 0;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_media_format_block_wmapro(struct audio_client *ac,
+ void *cfg)
+{
+ struct asm_stream_media_format_update fmt;
+ struct asm_wmapro_cfg *wmapro_cfg = (struct asm_wmapro_cfg *)cfg;
+ int rc = 0;
+
+ pr_debug("session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d],"
+ "balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x],\
+ adv_enc_opt[0x%4x], adv_enc_opt2[0x%8x]\n",
+ ac->session, wmapro_cfg->format_tag, wmapro_cfg->sample_rate,
+ wmapro_cfg->ch_cfg, wmapro_cfg->avg_bytes_per_sec,
+ wmapro_cfg->block_align, wmapro_cfg->valid_bits_per_sample,
+ wmapro_cfg->ch_mask, wmapro_cfg->encode_opt,
+ wmapro_cfg->adv_encode_opt, wmapro_cfg->adv_encode_opt2);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = WMA_V10PRO;
+ fmt.cfg_size = sizeof(struct asm_wmapro_cfg);
+ fmt.write_cfg.wmapro_cfg.format_tag = wmapro_cfg->format_tag;
+ fmt.write_cfg.wmapro_cfg.ch_cfg = wmapro_cfg->ch_cfg;
+ fmt.write_cfg.wmapro_cfg.sample_rate = wmapro_cfg->sample_rate;
+ fmt.write_cfg.wmapro_cfg.avg_bytes_per_sec =
+ wmapro_cfg->avg_bytes_per_sec;
+ fmt.write_cfg.wmapro_cfg.block_align = wmapro_cfg->block_align;
+ fmt.write_cfg.wmapro_cfg.valid_bits_per_sample =
+ wmapro_cfg->valid_bits_per_sample;
+ fmt.write_cfg.wmapro_cfg.ch_mask = wmapro_cfg->ch_mask;
+ fmt.write_cfg.wmapro_cfg.encode_opt = wmapro_cfg->encode_opt;
+ fmt.write_cfg.wmapro_cfg.adv_encode_opt = wmapro_cfg->adv_encode_opt;
+ fmt.write_cfg.wmapro_cfg.adv_encode_opt2 = wmapro_cfg->adv_encode_opt2;
+ fmt.write_cfg.wmapro_cfg.drc_peak_ref = 0;
+ fmt.write_cfg.wmapro_cfg.drc_peak_target = 0;
+ fmt.write_cfg.wmapro_cfg.drc_ave_ref = 0;
+ fmt.write_cfg.wmapro_cfg.drc_ave_target = 0;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_memory_map(struct audio_client *ac, uint32_t buf_add, int dir,
+ uint32_t bufsz, uint32_t bufcnt)
+{
+ struct asm_stream_cmd_memory_map mem_map;
+ int rc = 0;
+
+ if (!ac || ac->apr == NULL || this_mmap.apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ mem_map.hdr.opcode = ASM_SESSION_CMD_MEMORY_MAP;
+
+ mem_map.buf_add = buf_add;
+ mem_map.buf_size = bufsz * bufcnt;
+ mem_map.mempool_id = 0; /* EBI */
+ mem_map.reserved = 0;
+
+ q6asm_add_mmaphdr(&mem_map.hdr,
+ sizeof(struct asm_stream_cmd_memory_map), TRUE);
+
+ pr_debug("buf add[%x] buf_add_parameter[%x]\n",
+ mem_map.buf_add, buf_add);
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_map);
+ if (rc < 0) {
+ pr_err("mem_map op[0x%x]rc[%d]\n",
+ mem_map.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0), 5 * HZ);
+ if (!rc) {
+ pr_err("timeout. waited for memory_map\n");
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_memory_unmap(struct audio_client *ac, uint32_t buf_add, int dir)
+{
+ struct asm_stream_cmd_memory_unmap mem_unmap;
+ int rc = 0;
+
+ if (!ac || ac->apr == NULL || this_mmap.apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ q6asm_add_mmaphdr(&mem_unmap.hdr,
+ sizeof(struct asm_stream_cmd_memory_unmap), TRUE);
+ mem_unmap.hdr.opcode = ASM_SESSION_CMD_MEMORY_UNMAP;
+ mem_unmap.buf_add = buf_add;
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap);
+ if (rc < 0) {
+ pr_err("mem_unmap op[0x%x]rc[%d]\n",
+ mem_unmap.hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0), 5 * HZ);
+ if (!rc) {
+ pr_err("timeout. waited for memory_map\n");
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
+int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain)
+{
+ void *vol_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_lrchannel_gain_params *lrgain = NULL;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_lrchannel_gain_params);
+ vol_cmd = kzalloc(sz, GFP_KERNEL);
+ if (vol_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ return rc;
+ }
+ cmd = (struct asm_pp_params_command *)vol_cmd;
+ q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_lrchannel_gain_params);
+ cmd->params.module_id = VOLUME_CONTROL_MODULE_ID;
+ cmd->params.param_id = L_R_CHANNEL_GAIN_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_lrchannel_gain_params);
+ cmd->params.reserved = 0;
+
+ payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command));
+ lrgain = (struct asm_lrchannel_gain_params *)payload;
+
+ lrgain->left_gain = left_gain;
+ lrgain->right_gain = right_gain;
+ rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd);
+ if (rc < 0) {
+ pr_err("%s: Volume Command failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending volume command to apr\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(vol_cmd);
+ return rc;
+}
+
+static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
+ uint32_t bufsz, uint32_t bufcnt)
+{
+ struct asm_stream_cmd_memory_map_regions *mmap_regions = NULL;
+ struct asm_memory_map_regions *mregions = NULL;
+ struct audio_port_data *port = NULL;
+ struct audio_buffer *ab = NULL;
+ void *mmap_region_cmd = NULL;
+ void *payload = NULL;
+ int rc = 0;
+ int i = 0;
+ int cmd_size = 0;
+
+ if (!ac || ac->apr == NULL || this_mmap.apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ cmd_size = sizeof(struct asm_stream_cmd_memory_map_regions)
+ + sizeof(struct asm_memory_map_regions) * bufcnt;
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ mmap_regions = (struct asm_stream_cmd_memory_map_regions *)
+ mmap_region_cmd;
+ q6asm_add_mmaphdr(&mmap_regions->hdr, cmd_size, TRUE);
+ mmap_regions->hdr.opcode = ASM_SESSION_CMD_MEMORY_MAP_REGIONS;
+ mmap_regions->mempool_id = 0;
+ mmap_regions->nregions = bufcnt & 0x00ff;
+ pr_debug("map_regions->nregions = %d\n", mmap_regions->nregions);
+ payload = ((u8 *) mmap_region_cmd +
+ sizeof(struct asm_stream_cmd_memory_map_regions));
+ mregions = (struct asm_memory_map_regions *)payload;
+
+ port = &ac->port[dir];
+ for (i = 0; i < bufcnt; i++) {
+ ab = &port->buf[i];
+ mregions->phys = ab->phys;
+ mregions->buf_size = ab->size;
+ ++mregions;
+ }
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) mmap_region_cmd);
+ if (rc < 0) {
+ pr_err("mmap_regions op[0x%x]rc[%d]\n",
+ mmap_regions->hdr.opcode, rc);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for memory_map\n");
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(mmap_region_cmd);
+ return rc;
+}
+
+static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir,
+ uint32_t bufsz, uint32_t bufcnt)
+{
+ struct asm_stream_cmd_memory_unmap_regions *unmap_regions = NULL;
+ struct asm_memory_unmap_regions *mregions = NULL;
+ struct audio_port_data *port = NULL;
+ struct audio_buffer *ab = NULL;
+ void *unmap_region_cmd = NULL;
+ void *payload = NULL;
+ int rc = 0;
+ int i = 0;
+ int cmd_size = 0;
+
+ if (!ac || ac->apr == NULL || this_mmap.apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+ cmd_size = sizeof(struct asm_stream_cmd_memory_unmap_regions) +
+ sizeof(struct asm_memory_unmap_regions) * bufcnt;
+
+ unmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ unmap_regions = (struct asm_stream_cmd_memory_unmap_regions *)
+ unmap_region_cmd;
+ q6asm_add_mmaphdr(&unmap_regions->hdr, cmd_size, TRUE);
+ unmap_regions->hdr.opcode = ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS;
+ unmap_regions->nregions = bufcnt & 0x00ff;
+ pr_debug("unmap_regions->nregions = %d\n", unmap_regions->nregions);
+ payload = ((u8 *) unmap_region_cmd +
+ sizeof(struct asm_stream_cmd_memory_unmap_regions));
+ mregions = (struct asm_memory_unmap_regions *)payload;
+ port = &ac->port[dir];
+ for (i = 0; i < bufcnt; i++) {
+ ab = &port->buf[i];
+ mregions->phys = ab->phys;
+ ++mregions;
+ }
+
+ rc = apr_send_pkt(this_mmap.apr, (uint32_t *) unmap_region_cmd);
+ if (rc < 0) {
+ pr_err("mmap_regions op[0x%x]rc[%d]\n",
+ unmap_regions->hdr.opcode, rc);
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(this_mmap.cmd_wait,
+ (atomic_read(&this_mmap.cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for memory_unmap\n");
+ goto fail_cmd;
+ }
+ rc = 0;
+
+fail_cmd:
+ kfree(unmap_region_cmd);
+ return rc;
+}
+
+int q6asm_set_mute(struct audio_client *ac, int muteflag)
+{
+ void *vol_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_mute_params *mute = NULL;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_mute_params);
+ vol_cmd = kzalloc(sz, GFP_KERNEL);
+ if (vol_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ return rc;
+ }
+ cmd = (struct asm_pp_params_command *)vol_cmd;
+ q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_mute_params);
+ cmd->params.module_id = VOLUME_CONTROL_MODULE_ID;
+ cmd->params.param_id = MUTE_CONFIG_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_mute_params);
+ cmd->params.reserved = 0;
+
+ payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command));
+ mute = (struct asm_mute_params *)payload;
+
+ mute->muteflag = muteflag;
+ rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd);
+ if (rc < 0) {
+ pr_err("%s: Mute Command failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending mute command to apr\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(vol_cmd);
+ return rc;
+}
+
+int q6asm_set_volume(struct audio_client *ac, int volume)
+{
+ void *vol_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_master_gain_params *mgain = NULL;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_master_gain_params);
+ vol_cmd = kzalloc(sz, GFP_KERNEL);
+ if (vol_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ return rc;
+ }
+ cmd = (struct asm_pp_params_command *)vol_cmd;
+ q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_master_gain_params);
+ cmd->params.module_id = VOLUME_CONTROL_MODULE_ID;
+ cmd->params.param_id = MASTER_GAIN_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_master_gain_params);
+ cmd->params.reserved = 0;
+
+ payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command));
+ mgain = (struct asm_master_gain_params *)payload;
+
+ mgain->master_gain = volume;
+ mgain->padding = 0x00;
+ rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd);
+ if (rc < 0) {
+ pr_err("%s: Volume Command failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending volume command to apr\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(vol_cmd);
+ return rc;
+}
+
+int q6asm_set_softpause(struct audio_client *ac,
+ struct asm_softpause_params *pause_param)
+{
+ void *vol_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_softpause_params *params = NULL;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_softpause_params);
+ vol_cmd = kzalloc(sz, GFP_KERNEL);
+ if (vol_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ return rc;
+ }
+ cmd = (struct asm_pp_params_command *)vol_cmd;
+ q6asm_add_hdr_async(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_softpause_params);
+ cmd->params.module_id = VOLUME_CONTROL_MODULE_ID;
+ cmd->params.param_id = SOFT_PAUSE_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_softpause_params);
+ cmd->params.reserved = 0;
+
+ payload = (u8 *)(vol_cmd + sizeof(struct asm_pp_params_command));
+ params = (struct asm_softpause_params *)payload;
+
+ params->enable = pause_param->enable;
+ params->period = pause_param->period;
+ params->step = pause_param->step;
+ params->rampingcurve = pause_param->rampingcurve;
+ pr_debug("%s: soft Pause Command: enable = %d, period = %d,"
+ "step = %d, curve = %d\n", __func__, params->enable,
+ params->period, params->step, params->rampingcurve);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) vol_cmd);
+ if (rc < 0) {
+ pr_err("%s: Volume Command(soft_pause) failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending volume command(soft_pause)"
+ "to apr\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(vol_cmd);
+ return rc;
+}
+
+int q6asm_equalizer(struct audio_client *ac, void *eq)
+{
+ void *eq_cmd = NULL;
+ void *payload = NULL;
+ struct asm_pp_params_command *cmd = NULL;
+ struct asm_equalizer_params *equalizer = NULL;
+ struct msm_audio_eq_stream_config *eq_params = NULL;
+ int i = 0;
+ int sz = 0;
+ int rc = 0;
+
+ sz = sizeof(struct asm_pp_params_command) +
+ + sizeof(struct asm_equalizer_params);
+ eq_cmd = kzalloc(sz, GFP_KERNEL);
+ if (eq_cmd == NULL) {
+ pr_err("%s[%d]: Mem alloc failed\n", __func__, ac->session);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ eq_params = (struct msm_audio_eq_stream_config *) eq;
+ cmd = (struct asm_pp_params_command *)eq_cmd;
+ q6asm_add_hdr(ac, &cmd->hdr, sz, TRUE);
+ cmd->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS;
+ cmd->payload = NULL;
+ cmd->payload_size = sizeof(struct asm_pp_param_data_hdr) +
+ sizeof(struct asm_equalizer_params);
+ cmd->params.module_id = EQUALIZER_MODULE_ID;
+ cmd->params.param_id = EQUALIZER_PARAM_ID;
+ cmd->params.param_size = sizeof(struct asm_equalizer_params);
+ cmd->params.reserved = 0;
+ payload = (u8 *)(eq_cmd + sizeof(struct asm_pp_params_command));
+ equalizer = (struct asm_equalizer_params *)payload;
+
+ equalizer->enable = eq_params->enable;
+ equalizer->num_bands = eq_params->num_bands;
+ pr_debug("%s: enable:%d numbands:%d\n", __func__, eq_params->enable,
+ eq_params->num_bands);
+ for (i = 0; i < eq_params->num_bands; i++) {
+ equalizer->eq_bands[i].band_idx =
+ eq_params->eq_bands[i].band_idx;
+ equalizer->eq_bands[i].filter_type =
+ eq_params->eq_bands[i].filter_type;
+ equalizer->eq_bands[i].center_freq_hz =
+ eq_params->eq_bands[i].center_freq_hz;
+ equalizer->eq_bands[i].filter_gain =
+ eq_params->eq_bands[i].filter_gain;
+ equalizer->eq_bands[i].q_factor =
+ eq_params->eq_bands[i].q_factor;
+ pr_debug("%s: filter_type:%u bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].filter_type, i);
+ pr_debug("%s: center_freq_hz:%u bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].center_freq_hz, i);
+ pr_debug("%s: filter_gain:%d bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].filter_gain, i);
+ pr_debug("%s: q_factor:%d bandnum:%d\n", __func__,
+ eq_params->eq_bands[i].q_factor, i);
+ }
+ rc = apr_send_pkt(ac->apr, (uint32_t *) eq_cmd);
+ if (rc < 0) {
+ pr_err("%s: Equalizer Command failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in sending equalizer command to apr\n",
+ __func__);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ kfree(eq_cmd);
+ return rc;
+}
+
+int q6asm_read(struct audio_client *ac)
+{
+ struct asm_stream_cmd_read read;
+ struct audio_buffer *ab;
+ int dsp_buf;
+ struct audio_port_data *port;
+ int rc;
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ if (ac->io_mode == SYNC_IO_MODE) {
+ port = &ac->port[OUT];
+
+ q6asm_add_hdr(ac, &read.hdr, sizeof(read), FALSE);
+
+ mutex_lock(&port->lock);
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ pr_debug("%s:session[%d]dsp-buf[%d][%p]cpu_buf[%d][%p]\n",
+ __func__,
+ ac->session,
+ dsp_buf,
+ (void *)port->buf[dsp_buf].data,
+ port->cpu_buf,
+ (void *)port->buf[port->cpu_buf].phys);
+
+ read.hdr.opcode = ASM_DATA_CMD_READ;
+ read.buf_add = ab->phys;
+ read.buf_size = ab->size;
+ read.uid = port->dsp_buf;
+ read.hdr.token = port->dsp_buf;
+
+ port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1);
+ mutex_unlock(&port->lock);
+ pr_debug("%s:buf add[0x%x] token[%d] uid[%d]\n", __func__,
+ read.buf_add,
+ read.hdr.token,
+ read.uid);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+ if (rc < 0) {
+ pr_err("read op[0x%x]rc[%d]\n", read.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_read_nolock(struct audio_client *ac)
+{
+ struct asm_stream_cmd_read read;
+ struct audio_buffer *ab;
+ int dsp_buf;
+ struct audio_port_data *port;
+ int rc;
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ if (ac->io_mode == SYNC_IO_MODE) {
+ port = &ac->port[OUT];
+
+ q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE);
+
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ pr_debug("%s:session[%d]dsp-buf[%d][%p]cpu_buf[%d][%p]\n",
+ __func__,
+ ac->session,
+ dsp_buf,
+ (void *)port->buf[dsp_buf].data,
+ port->cpu_buf,
+ (void *)port->buf[port->cpu_buf].phys);
+
+ read.hdr.opcode = ASM_DATA_CMD_READ;
+ read.buf_add = ab->phys;
+ read.buf_size = ab->size;
+ read.uid = port->dsp_buf;
+ read.hdr.token = port->dsp_buf;
+
+ port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1);
+ pr_debug("%s:buf add[0x%x] token[%d] uid[%d]\n", __func__,
+ read.buf_add,
+ read.hdr.token,
+ read.uid);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+ if (rc < 0) {
+ pr_err("read op[0x%x]rc[%d]\n", read.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+
+static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr,
+ uint32_t pkt_size, uint32_t cmd_flg)
+{
+ pr_debug("session=%d pkt size=%d cmd_flg=%d\n", pkt_size, cmd_flg,
+ ac->session);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \
+ APR_HDR_LEN(sizeof(struct apr_hdr)),\
+ APR_PKT_VER);
+ hdr->src_svc = ((struct apr_svc *)ac->apr)->id;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_ASM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = ((ac->session << 8) & 0xFF00) | 0x01;
+ hdr->dest_port = ((ac->session << 8) & 0xFF00) | 0x01;
+ if (cmd_flg) {
+ hdr->token = ac->session;
+ atomic_set(&ac->cmd_state, 1);
+ }
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+int q6asm_async_write(struct audio_client *ac,
+ struct audio_aio_write_param *param)
+{
+ int rc = 0;
+ struct asm_stream_cmd_write write;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6asm_add_hdr_async(ac, &write.hdr, sizeof(write), FALSE);
+
+ /* Pass physical address as token for AIO scheme */
+ write.hdr.token = param->uid;
+ write.hdr.opcode = ASM_DATA_CMD_WRITE;
+ write.buf_add = param->paddr;
+ write.avail_bytes = param->len;
+ write.uid = param->uid;
+ write.msw_ts = param->msw_ts;
+ write.lsw_ts = param->lsw_ts;
+ /* Use 0xFF00 for disabling timestamps */
+ if (param->flags == 0xFF00)
+ write.uflags = (0x00000000 | (param->flags & 0x800000FF));
+ else
+ write.uflags = (0x80000000 | param->flags);
+
+ pr_debug("%s: session[%d] bufadd[0x%x]len[0x%x]", __func__, ac->session,
+ write.buf_add, write.avail_bytes);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+ if (rc < 0) {
+ pr_debug("[%s] write op[0x%x]rc[%d]\n", __func__,
+ write.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_async_read(struct audio_client *ac,
+ struct audio_aio_read_param *param)
+{
+ int rc = 0;
+ struct asm_stream_cmd_read read;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("%s: APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE);
+
+ /* Pass physical address as token for AIO scheme */
+ read.hdr.token = param->paddr;
+ read.hdr.opcode = ASM_DATA_CMD_READ;
+ read.buf_add = param->paddr;
+ read.buf_size = param->len;
+ read.uid = param->uid;
+
+ pr_debug("%s: session[%d] bufadd[0x%x]len[0x%x]", __func__, ac->session,
+ read.buf_add, read.buf_size);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+ if (rc < 0) {
+ pr_debug("[%s] read op[0x%x]rc[%d]\n", __func__,
+ read.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t flags)
+{
+ int rc = 0;
+ struct asm_stream_cmd_write write;
+ struct audio_port_data *port;
+ struct audio_buffer *ab;
+ int dsp_buf = 0;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d] len=%d", __func__, ac->session, len);
+ if (ac->io_mode == SYNC_IO_MODE) {
+ port = &ac->port[IN];
+
+ q6asm_add_hdr(ac, &write.hdr, sizeof(write),
+ FALSE);
+ mutex_lock(&port->lock);
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ write.hdr.token = port->dsp_buf;
+ write.hdr.opcode = ASM_DATA_CMD_WRITE;
+ write.buf_add = ab->phys;
+ write.avail_bytes = len;
+ write.uid = port->dsp_buf;
+ write.msw_ts = msw_ts;
+ write.lsw_ts = lsw_ts;
+ /* Use 0xFF00 for disabling timestamps */
+ if (flags == 0xFF00)
+ write.uflags = (0x00000000 | (flags & 0x800000FF));
+ else
+ write.uflags = (0x80000000 | flags);
+ port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1);
+
+ pr_debug("%s:ab->phys[0x%x]bufadd[0x%x]token[0x%x]buf_id[0x%x]"
+ , __func__,
+ ab->phys,
+ write.buf_add,
+ write.hdr.token,
+ write.uid);
+ mutex_unlock(&port->lock);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+ if (rc < 0) {
+ pr_err("write op[0x%x]rc[%d]\n", write.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ pr_debug("%s: WRITE SUCCESS\n", __func__);
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t flags)
+{
+ int rc = 0;
+ struct asm_stream_cmd_write write;
+ struct audio_port_data *port;
+ struct audio_buffer *ab;
+ int dsp_buf = 0;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d] len=%d", __func__, ac->session, len);
+ if (ac->io_mode == SYNC_IO_MODE) {
+ port = &ac->port[IN];
+
+ q6asm_add_hdr_async(ac, &write.hdr, sizeof(write),
+ FALSE);
+
+ dsp_buf = port->dsp_buf;
+ ab = &port->buf[dsp_buf];
+
+ write.hdr.token = port->dsp_buf;
+ write.hdr.opcode = ASM_DATA_CMD_WRITE;
+ write.buf_add = ab->phys;
+ write.avail_bytes = len;
+ write.uid = port->dsp_buf;
+ write.msw_ts = msw_ts;
+ write.lsw_ts = lsw_ts;
+ /* Use 0xFF00 for disabling timestamps */
+ if (flags == 0xFF00)
+ write.uflags = (0x00000000 | (flags & 0x800000FF));
+ else
+ write.uflags = (0x80000000 | flags);
+ port->dsp_buf = (port->dsp_buf + 1) & (port->max_buf_cnt - 1);
+
+ pr_debug("%s:ab->phys[0x%x]bufadd[0x%x]token[0x%x]buf_id[0x%x]"
+ , __func__,
+ ab->phys,
+ write.buf_add,
+ write.hdr.token,
+ write.uid);
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+ if (rc < 0) {
+ pr_err("write op[0x%x]rc[%d]\n", write.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ pr_debug("%s: WRITE SUCCESS\n", __func__);
+ return 0;
+ }
+fail_cmd:
+ return -EINVAL;
+}
+
+uint64_t q6asm_get_session_time(struct audio_client *ac)
+{
+ struct apr_hdr hdr;
+ int rc;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE);
+ hdr.opcode = ASM_SESSION_CMD_GET_SESSION_TIME;
+ atomic_set(&ac->time_flag, 1);
+
+ pr_debug("%s: session[%d]opcode[0x%x]\n", __func__,
+ ac->session,
+ hdr.opcode);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("Commmand 0x%x failed\n", hdr.opcode);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->time_wait,
+ (atomic_read(&ac->time_flag) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout in getting session time from DSP\n",
+ __func__);
+ goto fail_cmd;
+ }
+ return ac->time_stamp;
+
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_cmd(struct audio_client *ac, int cmd)
+{
+ struct apr_hdr hdr;
+ int rc;
+ atomic_t *state;
+ int cnt = 0;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE);
+ switch (cmd) {
+ case CMD_PAUSE:
+ pr_debug("%s:CMD_PAUSE\n", __func__);
+ hdr.opcode = ASM_SESSION_CMD_PAUSE;
+ state = &ac->cmd_state;
+ break;
+ case CMD_FLUSH:
+ pr_debug("%s:CMD_FLUSH\n", __func__);
+ hdr.opcode = ASM_STREAM_CMD_FLUSH;
+ state = &ac->cmd_state;
+ break;
+ case CMD_OUT_FLUSH:
+ pr_debug("%s:CMD_OUT_FLUSH\n", __func__);
+ hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS;
+ state = &ac->cmd_state;
+ break;
+ case CMD_EOS:
+ pr_debug("%s:CMD_EOS\n", __func__);
+ hdr.opcode = ASM_DATA_CMD_EOS;
+ atomic_set(&ac->cmd_state, 0);
+ state = &ac->cmd_state;
+ break;
+ case CMD_CLOSE:
+ pr_debug("%s:CMD_CLOSE\n", __func__);
+ hdr.opcode = ASM_STREAM_CMD_CLOSE;
+ state = &ac->cmd_state;
+ break;
+ default:
+ pr_err("Invalid format[%d]\n", cmd);
+ goto fail_cmd;
+ }
+ pr_debug("%s:session[%d]opcode[0x%x] ", __func__,
+ ac->session,
+ hdr.opcode);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("Commmand 0x%x failed\n", hdr.opcode);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait, (atomic_read(state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for response opcode[0x%x]\n",
+ hdr.opcode);
+ goto fail_cmd;
+ }
+ if (cmd == CMD_FLUSH)
+ q6asm_reset_buf_state(ac);
+ if (cmd == CMD_CLOSE) {
+ /* check if DSP return all buffers */
+ if (ac->port[IN].buf) {
+ for (cnt = 0; cnt < ac->port[IN].max_buf_cnt;
+ cnt++) {
+ if (ac->port[IN].buf[cnt].used == IN) {
+ pr_debug("Write Buf[%d] not returned\n",
+ cnt);
+ }
+ }
+ }
+ if (ac->port[OUT].buf) {
+ for (cnt = 0; cnt < ac->port[OUT].max_buf_cnt; cnt++) {
+ if (ac->port[OUT].buf[cnt].used == OUT) {
+ pr_debug("Read Buf[%d] not returned\n",
+ cnt);
+ }
+ }
+ }
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+int q6asm_cmd_nowait(struct audio_client *ac, int cmd)
+{
+ struct apr_hdr hdr;
+ int rc;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("%s:APR handle NULL\n", __func__);
+ return -EINVAL;
+ }
+ q6asm_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE);
+ switch (cmd) {
+ case CMD_PAUSE:
+ pr_debug("%s:CMD_PAUSE\n", __func__);
+ hdr.opcode = ASM_SESSION_CMD_PAUSE;
+ break;
+ case CMD_EOS:
+ pr_debug("%s:CMD_EOS\n", __func__);
+ hdr.opcode = ASM_DATA_CMD_EOS;
+ break;
+ default:
+ pr_err("%s:Invalid format[%d]\n", __func__, cmd);
+ goto fail_cmd;
+ }
+ pr_debug("%s:session[%d]opcode[0x%x] ", __func__,
+ ac->session,
+ hdr.opcode);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+ if (rc < 0) {
+ pr_err("%s:Commmand 0x%x failed\n", __func__, hdr.opcode);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+static void q6asm_reset_buf_state(struct audio_client *ac)
+{
+ int cnt = 0;
+ int loopcnt = 0;
+ struct audio_port_data *port = NULL;
+
+ if (ac->io_mode == SYNC_IO_MODE) {
+ mutex_lock(&ac->cmd_lock);
+ for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
+ port = &ac->port[loopcnt];
+ cnt = port->max_buf_cnt - 1;
+ port->dsp_buf = 0;
+ port->cpu_buf = 0;
+ while (cnt >= 0) {
+ if (!port->buf)
+ continue;
+ port->buf[cnt].used = 1;
+ cnt--;
+ }
+ }
+ mutex_unlock(&ac->cmd_lock);
+ }
+}
+
+int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable)
+{
+ struct asm_stream_cmd_reg_tx_overflow_event tx_overflow;
+ int rc;
+
+ if (!ac || ac->apr == NULL) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s:session[%d]enable[%d]\n", __func__,
+ ac->session, enable);
+ q6asm_add_hdr(ac, &tx_overflow.hdr, sizeof(tx_overflow), TRUE);
+
+ tx_overflow.hdr.opcode = \
+ ASM_SESSION_CMD_REGISTER_FOR_TX_OVERFLOW_EVENTS;
+ /* tx overflow event: enable */
+ tx_overflow.enable = enable;
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &tx_overflow);
+ if (rc < 0) {
+ pr_err("tx overflow op[0x%x]rc[%d]\n", \
+ tx_overflow.hdr.opcode, rc);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("timeout. waited for tx overflow\n");
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
+#ifdef CONFIG_MSM8X60_RTAC
+int q6asm_get_apr_service_id(int session_id)
+{
+ pr_debug("%s\n", __func__);
+
+ if (session_id < 0) {
+ pr_err("%s: invalid session_id = %d\n", __func__, session_id);
+ return -EINVAL;
+ }
+
+ return ((struct apr_svc *)session[session_id]->apr)->id;
+}
+#endif
+
+
+static int __init q6asm_init(void)
+{
+ pr_debug("%s\n", __func__);
+ init_waitqueue_head(&this_mmap.cmd_wait);
+ memset(session, 0, sizeof(session));
+ return 0;
+}
+
+device_initcall(q6asm_init);
diff --git a/sound/soc/msm/qdsp6/q6voice.c b/sound/soc/msm/qdsp6/q6voice.c
new file mode 100644
index 0000000..28d1acf
--- /dev/null
+++ b/sound/soc/msm/qdsp6/q6voice.c
@@ -0,0 +1,2089 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <mach/qdsp6v2/audio_acdb.h>
+#include "sound/apr_audio.h"
+#include "sound/q6afe.h"
+#include "q6voice.h"
+
+#define TIMEOUT_MS 3000
+
+#define CMD_STATUS_SUCCESS 0
+#define CMD_STATUS_FAIL 1
+
+#define VOC_PATH_PASSIVE 0
+#define VOC_PATH_FULL 1
+
+static struct voice_data voice;
+
+static int voice_send_enable_vocproc_cmd(struct voice_data *v);
+static int voice_send_netid_timing_cmd(struct voice_data *v);
+static int voice_send_attach_vocproc_cmd(struct voice_data *v);
+static int voice_send_set_device_cmd(struct voice_data *v);
+static int voice_send_disable_vocproc_cmd(struct voice_data *v);
+static int voice_send_vol_index_cmd(struct voice_data *v);
+
+static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv);
+static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv);
+static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv);
+
+static u16 voice_get_mvm_handle(struct voice_data *v)
+{
+ u16 mvm_handle = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (v->voc_path == VOC_PATH_PASSIVE)
+ mvm_handle = v->mvm_passive_handle;
+ else
+ mvm_handle = v->mvm_full_handle;
+
+ pr_debug("%s: mvm_handle %d\n", __func__, mvm_handle);
+
+ return mvm_handle;
+}
+
+static void voice_set_mvm_handle(struct voice_data *v, u16 mvm_handle)
+{
+ pr_debug("%s: mvm_handle %d\n", __func__, mvm_handle);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return;
+ }
+
+ if (v->voc_path == VOC_PATH_PASSIVE)
+ v->mvm_passive_handle = mvm_handle;
+ else
+ v->mvm_full_handle = mvm_handle;
+}
+
+static u16 voice_get_cvs_handle(struct voice_data *v)
+{
+ u16 cvs_handle = 0;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ if (v->voc_path == VOC_PATH_PASSIVE)
+ cvs_handle = v->cvs_passive_handle;
+ else
+ cvs_handle = v->cvs_full_handle;
+
+ pr_debug("%s: cvs_handle %d\n", __func__, cvs_handle);
+
+ return cvs_handle;
+}
+
+static void voice_set_cvs_handle(struct voice_data *v, u16 cvs_handle)
+{
+ pr_debug("%s: cvs_handle %d\n", __func__, cvs_handle);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return;
+ }
+ if (v->voc_path == VOC_PATH_PASSIVE)
+ v->cvs_passive_handle = cvs_handle;
+ else
+ v->cvs_full_handle = cvs_handle;
+}
+
+static u16 voice_get_cvp_handle(struct voice_data *v)
+{
+ u16 cvp_handle = 0;
+
+ if (v->voc_path == VOC_PATH_PASSIVE)
+ cvp_handle = v->cvp_passive_handle;
+ else
+ cvp_handle = v->cvp_full_handle;
+
+ pr_debug("%s: cvp_handle %d\n", __func__, cvp_handle);
+
+ return cvp_handle;
+}
+
+static void voice_set_cvp_handle(struct voice_data *v, u16 cvp_handle)
+{
+ pr_debug("%s: cvp_handle %d\n", __func__, cvp_handle);
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return;
+ }
+ if (v->voc_path == VOC_PATH_PASSIVE)
+ v->cvp_passive_handle = cvp_handle;
+ else
+ v->cvp_full_handle = cvp_handle;
+}
+
+static int voice_apr_register(struct voice_data *v)
+{
+ void *apr_mvm, *apr_cvs, *apr_cvp;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = v->apr_q6_mvm;
+ apr_cvs = v->apr_q6_cvs;
+ apr_cvp = v->apr_q6_cvp;
+
+ pr_debug("into voice_apr_register_callback\n");
+ /* register callback to APR */
+ if (apr_mvm == NULL) {
+ pr_debug("start to register MVM callback\n");
+
+ apr_mvm = apr_register("ADSP", "MVM",
+ qdsp_mvm_callback,
+ 0xFFFFFFFF, v);
+
+ if (apr_mvm == NULL) {
+ pr_err("Unable to register MVM\n");
+ goto err;
+ }
+ v->apr_q6_mvm = apr_mvm;
+ }
+
+ if (apr_cvs == NULL) {
+ pr_debug("start to register CVS callback\n");
+
+ apr_cvs = apr_register("ADSP", "CVS",
+ qdsp_cvs_callback,
+ 0xFFFFFFFF, v);
+
+ if (apr_cvs == NULL) {
+ pr_err("Unable to register CVS\n");
+ goto err;
+ }
+ v->apr_q6_cvs = apr_cvs;
+ }
+
+ if (apr_cvp == NULL) {
+ pr_debug("start to register CVP callback\n");
+
+ apr_cvp = apr_register("ADSP", "CVP",
+ qdsp_cvp_callback,
+ 0xFFFFFFFF, v);
+
+ if (apr_cvp == NULL) {
+ pr_err("Unable to register CVP\n");
+ goto err;
+ }
+ v->apr_q6_cvp = apr_cvp;
+ }
+ return 0;
+
+err:
+ if (v->apr_q6_cvs != NULL) {
+ apr_deregister(apr_cvs);
+ v->apr_q6_cvs = NULL;
+ }
+ if (v->apr_q6_mvm != NULL) {
+ apr_deregister(apr_mvm);
+ v->apr_q6_mvm = NULL;
+ }
+
+ return -ENODEV;
+}
+
+static int voice_create_mvm_cvs_session(struct voice_data *v)
+{
+ int ret = 0;
+ struct mvm_create_passive_ctl_session_cmd mvm_session_cmd;
+ struct cvs_create_passive_ctl_session_cmd cvs_session_cmd;
+ struct mvm_create_full_ctl_session_cmd mvm_full_ctl_cmd;
+ struct cvs_create_full_ctl_session_cmd cvs_full_ctl_cmd;
+ struct mvm_attach_stream_cmd attach_stream_cmd;
+ void *apr_mvm, *apr_cvs, *apr_cvp;
+ u16 mvm_handle, cvs_handle, cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = v->apr_q6_mvm;
+ apr_cvs = v->apr_q6_cvs;
+ apr_cvp = v->apr_q6_cvp;
+
+ if (!apr_mvm || !apr_cvs || !apr_cvp) {
+ pr_err("%s: apr_mvm or apr_cvs or apr_cvp is NULL\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+ cvs_handle = voice_get_cvs_handle(v);
+ cvp_handle = voice_get_cvp_handle(v);
+
+ pr_debug("%s: mvm_hdl=%d, cvs_hdl=%d\n", __func__,
+ mvm_handle, cvs_handle);
+ /* send cmd to create mvm session and wait for response */
+
+ if (!mvm_handle) {
+ if (v->voc_path == VOC_PATH_PASSIVE) {
+ mvm_session_cmd.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_session_cmd.hdr.pkt_size = APR_PKT_SIZE(
+ APR_HDR_SIZE,
+ sizeof(mvm_session_cmd) -
+ APR_HDR_SIZE);
+ pr_debug("send mvm create session pkt size = %d\n",
+ mvm_session_cmd.hdr.pkt_size);
+ mvm_session_cmd.hdr.src_port = 0;
+ mvm_session_cmd.hdr.dest_port = 0;
+ mvm_session_cmd.hdr.token = 0;
+ mvm_session_cmd.hdr.opcode =
+ VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION;
+ v->mvm_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_mvm,
+ (uint32_t *) &mvm_session_cmd);
+ if (ret < 0) {
+ pr_err("Error sending MVM_CONTROL_SESSION\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ } else {
+ pr_debug("%s: creating MVM full ctrl\n", __func__);
+ mvm_full_ctl_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mvm_full_ctl_cmd.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_full_ctl_cmd) -
+ APR_HDR_SIZE);
+ mvm_full_ctl_cmd.hdr.src_port = 0;
+ mvm_full_ctl_cmd.hdr.dest_port = 0;
+ mvm_full_ctl_cmd.hdr.token = 0;
+ mvm_full_ctl_cmd.hdr.opcode =
+ VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION;
+ strncpy(mvm_full_ctl_cmd.mvm_session.name,
+ "default voip", 12);
+
+ v->mvm_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_mvm,
+ (uint32_t *) &mvm_full_ctl_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending MVM_CONTROL_SESSION\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ }
+ /* Get the created MVM handle. */
+ mvm_handle = voice_get_mvm_handle(v);
+ }
+ /* send cmd to create cvs session */
+ if (!cvs_handle) {
+ if (v->voc_path == VOC_PATH_PASSIVE) {
+ pr_debug("creating CVS passive session\n");
+
+ cvs_session_cmd.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_session_cmd.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_session_cmd) -
+ APR_HDR_SIZE);
+ cvs_session_cmd.hdr.src_port = 0;
+ cvs_session_cmd.hdr.dest_port = 0;
+ cvs_session_cmd.hdr.token = 0;
+ cvs_session_cmd.hdr.opcode =
+ VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION;
+ strncpy(cvs_session_cmd.cvs_session.name,
+ "default modem voice", 19);
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs,
+ (uint32_t *) &cvs_session_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending STREAM_CONTROL_SESSION\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ /* Get the created CVS handle. */
+ cvs_handle = voice_get_cvs_handle(v);
+
+ } else {
+ pr_debug("creating CVS full session\n");
+
+ cvs_full_ctl_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+
+ cvs_full_ctl_cmd.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_full_ctl_cmd) -
+ APR_HDR_SIZE);
+
+ cvs_full_ctl_cmd.hdr.src_port = 0;
+ cvs_full_ctl_cmd.hdr.dest_port = 0;
+ cvs_full_ctl_cmd.hdr.token = 0;
+ cvs_full_ctl_cmd.hdr.opcode =
+ VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION;
+ cvs_full_ctl_cmd.cvs_session.direction = 2;
+ cvs_full_ctl_cmd.cvs_session.enc_media_type =
+ v->mvs_info.media_type;
+ cvs_full_ctl_cmd.cvs_session.dec_media_type =
+ v->mvs_info.media_type;
+ cvs_full_ctl_cmd.cvs_session.network_id =
+ v->mvs_info.network_type;
+ strncpy(cvs_full_ctl_cmd.cvs_session.name,
+ "default q6 voice", 16);
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs,
+ (uint32_t *) &cvs_full_ctl_cmd);
+
+ if (ret < 0) {
+ pr_err("%s: Err %d sending CREATE_FULL_CTRL\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ /* Get the created CVS handle. */
+ cvs_handle = voice_get_cvs_handle(v);
+
+ /* Attach MVM to CVS. */
+ pr_debug("Attach MVM to stream\n");
+
+ attach_stream_cmd.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ attach_stream_cmd.hdr.pkt_size =
+ APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(attach_stream_cmd) -
+ APR_HDR_SIZE);
+ attach_stream_cmd.hdr.src_port = 0;
+ attach_stream_cmd.hdr.dest_port = mvm_handle;
+ attach_stream_cmd.hdr.token = 0;
+ attach_stream_cmd.hdr.opcode =
+ VSS_IMVM_CMD_ATTACH_STREAM;
+ attach_stream_cmd.attach_stream.handle = cvs_handle;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_mvm,
+ (uint32_t *) &attach_stream_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending ATTACH_STREAM\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ }
+ }
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_destroy_mvm_cvs_session(struct voice_data *v)
+{
+ int ret = 0;
+ struct mvm_detach_stream_cmd detach_stream;
+ struct apr_hdr mvm_destroy;
+ struct apr_hdr cvs_destroy;
+ void *apr_mvm, *apr_cvs;
+ u16 mvm_handle, cvs_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = v->apr_q6_mvm;
+ apr_cvs = v->apr_q6_cvs;
+
+ if (!apr_mvm || !apr_cvs) {
+ pr_err("%s: apr_mvm or apr_cvs is NULL\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+ cvs_handle = voice_get_cvs_handle(v);
+
+ /* MVM, CVS sessions are destroyed only for Full control sessions. */
+ if (v->voc_path == VOC_PATH_FULL) {
+ pr_debug("MVM detach stream\n");
+
+ /* Detach voice stream. */
+ detach_stream.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ detach_stream.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(detach_stream) - APR_HDR_SIZE);
+ detach_stream.hdr.src_port = 0;
+ detach_stream.hdr.dest_port = mvm_handle;
+ detach_stream.hdr.token = 0;
+ detach_stream.hdr.opcode = VSS_IMVM_CMD_DETACH_STREAM;
+ detach_stream.detach_stream.handle = cvs_handle;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &detach_stream);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending DETACH_STREAM\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait event timeout\n", __func__);
+ goto fail;
+ }
+ /* Destroy CVS. */
+ pr_debug("CVS destroy session\n");
+
+ cvs_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_destroy) - APR_HDR_SIZE);
+ cvs_destroy.src_port = 0;
+ cvs_destroy.dest_port = cvs_handle;
+ cvs_destroy.token = 0;
+ cvs_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_destroy);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending CVS DESTROY\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait event timeout\n", __func__);
+
+ goto fail;
+ }
+ cvs_handle = 0;
+ voice_set_cvs_handle(v, cvs_handle);
+
+ /* Destroy MVM. */
+ pr_debug("MVM destroy session\n");
+
+ mvm_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_destroy) - APR_HDR_SIZE);
+ mvm_destroy.src_port = 0;
+ mvm_destroy.dest_port = mvm_handle;
+ mvm_destroy.token = 0;
+ mvm_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_destroy);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending MVM DESTROY\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait event timeout\n", __func__);
+
+ goto fail;
+ }
+ mvm_handle = 0;
+ voice_set_mvm_handle(v, mvm_handle);
+ }
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_tty_mode_cmd(struct voice_data *v)
+{
+ int tty_mode = 0;
+ int ret = 0;
+ struct mvm_set_tty_mode_cmd mvm_tty_mode_cmd;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = v->apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ if (tty_mode) {
+ /* send tty mode cmd to mvm */
+ mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_tty_mode_cmd) -
+ APR_HDR_SIZE);
+ pr_debug("pkt size = %d\n", mvm_tty_mode_cmd.hdr.pkt_size);
+ mvm_tty_mode_cmd.hdr.src_port = 0;
+ mvm_tty_mode_cmd.hdr.dest_port = mvm_handle;
+ mvm_tty_mode_cmd.hdr.token = 0;
+ mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE;
+ mvm_tty_mode_cmd.tty_mode.mode = tty_mode;
+ pr_debug("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode);
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd);
+ if (ret < 0) {
+ pr_err("Fail: sending VSS_ISTREAM_CMD_SET_TTY_MODE\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ }
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static int voice_config_cvs_vocoder(struct voice_data *v)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+ /* Set media type. */
+ struct cvs_set_media_type_cmd cvs_set_media_cmd;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = v->apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ cvs_set_media_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_media_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_media_cmd) - APR_HDR_SIZE);
+ cvs_set_media_cmd.hdr.src_port = 0;
+ cvs_set_media_cmd.hdr.dest_port = cvs_handle;
+ cvs_set_media_cmd.hdr.token = 0;
+ cvs_set_media_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MEDIA_TYPE;
+ cvs_set_media_cmd.media_type.tx_media_id = v->mvs_info.media_type;
+ cvs_set_media_cmd.media_type.rx_media_id = v->mvs_info.media_type;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_media_cmd);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_MEDIA_TYPE\n",
+ __func__, ret);
+
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+ /* Set encoder properties. */
+ switch (v->mvs_info.media_type) {
+ case VSS_MEDIA_ID_EVRC_MODEM: {
+ struct cvs_set_cdma_enc_minmax_rate_cmd cvs_set_cdma_rate;
+
+ pr_debug("Setting EVRC min-max rate\n");
+
+ cvs_set_cdma_rate.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_cdma_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_cdma_rate) - APR_HDR_SIZE);
+ cvs_set_cdma_rate.hdr.src_port = 0;
+ cvs_set_cdma_rate.hdr.dest_port = cvs_handle;
+ cvs_set_cdma_rate.hdr.token = 0;
+ cvs_set_cdma_rate.hdr.opcode =
+ VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE;
+ cvs_set_cdma_rate.cdma_rate.min_rate = v->mvs_info.rate;
+ cvs_set_cdma_rate.cdma_rate.max_rate = v->mvs_info.rate;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_cdma_rate);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_EVRC_MINMAX_RATE\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+ break;
+ }
+ case VSS_MEDIA_ID_AMR_NB_MODEM: {
+ struct cvs_set_amr_enc_rate_cmd cvs_set_amr_rate;
+ struct cvs_set_enc_dtx_mode_cmd cvs_set_dtx;
+
+ pr_debug("Setting AMR rate\n");
+
+ cvs_set_amr_rate.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_amr_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_amr_rate) - APR_HDR_SIZE);
+ cvs_set_amr_rate.hdr.src_port = 0;
+ cvs_set_amr_rate.hdr.dest_port = cvs_handle;
+ cvs_set_amr_rate.hdr.token = 0;
+ cvs_set_amr_rate.hdr.opcode =
+ VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE;
+ cvs_set_amr_rate.amr_rate.mode = v->mvs_info.rate;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amr_rate);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_AMR_RATE\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ /* Disable DTX */
+ pr_debug("Disabling DTX\n");
+
+ cvs_set_dtx.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_dtx.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_dtx) - APR_HDR_SIZE);
+ cvs_set_dtx.hdr.src_port = 0;
+ cvs_set_dtx.hdr.dest_port = cvs_handle;
+ cvs_set_dtx.hdr.token = 0;
+ cvs_set_dtx.hdr.opcode = VSS_ISTREAM_CMD_SET_ENC_DTX_MODE;
+ cvs_set_dtx.dtx_mode.enable = 0;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_dtx);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_DTX\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ break;
+ }
+ case VSS_MEDIA_ID_AMR_WB_MODEM: {
+ struct cvs_set_amrwb_enc_rate_cmd cvs_set_amrwb_rate;
+ struct cvs_set_enc_dtx_mode_cmd cvs_set_dtx;
+
+ pr_debug("Setting AMR WB rate\n");
+
+ cvs_set_amrwb_rate.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_amrwb_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_amrwb_rate) -
+ APR_HDR_SIZE);
+ cvs_set_amrwb_rate.hdr.src_port = 0;
+ cvs_set_amrwb_rate.hdr.dest_port = cvs_handle;
+ cvs_set_amrwb_rate.hdr.token = 0;
+ cvs_set_amrwb_rate.hdr.opcode =
+ VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE;
+ cvs_set_amrwb_rate.amrwb_rate.mode = v->mvs_info.rate;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amrwb_rate);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_AMRWB_RATE\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ /* Disable DTX */
+ pr_debug("Disabling DTX\n");
+
+ cvs_set_dtx.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_dtx.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_dtx) - APR_HDR_SIZE);
+ cvs_set_dtx.hdr.src_port = 0;
+ cvs_set_dtx.hdr.dest_port = cvs_handle;
+ cvs_set_dtx.hdr.token = 0;
+ cvs_set_dtx.hdr.opcode = VSS_ISTREAM_CMD_SET_ENC_DTX_MODE;
+ cvs_set_dtx.dtx_mode.enable = 0;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_dtx);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_DTX\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ break;
+ }
+ case VSS_MEDIA_ID_G729:
+ case VSS_MEDIA_ID_G711_ALAW:
+ case VSS_MEDIA_ID_G711_MULAW: {
+ struct cvs_set_enc_dtx_mode_cmd cvs_set_dtx;
+ /* Disable DTX */
+ pr_debug("Disabling DTX\n");
+
+ cvs_set_dtx.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_set_dtx.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_set_dtx) - APR_HDR_SIZE);
+ cvs_set_dtx.hdr.src_port = 0;
+ cvs_set_dtx.hdr.dest_port = cvs_handle;
+ cvs_set_dtx.hdr.token = 0;
+ cvs_set_dtx.hdr.opcode = VSS_ISTREAM_CMD_SET_ENC_DTX_MODE;
+ cvs_set_dtx.dtx_mode.enable = 0;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_dtx);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_DTX\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ break;
+ }
+ default:
+ /* Do nothing. */
+ break;
+ }
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_start_voice_cmd(struct voice_data *v)
+{
+ struct apr_hdr mvm_start_voice_cmd;
+ int ret = 0;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = v->apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ mvm_start_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_start_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_start_voice_cmd) - APR_HDR_SIZE);
+ pr_debug("send mvm_start_voice_cmd pkt size = %d\n",
+ mvm_start_voice_cmd.pkt_size);
+ mvm_start_voice_cmd.src_port = 0;
+ mvm_start_voice_cmd.dest_port = mvm_handle;
+ mvm_start_voice_cmd.token = 0;
+ mvm_start_voice_cmd.opcode = VSS_IMVM_CMD_START_VOICE;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_start_voice_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IMVM_CMD_START_VOICE\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_disable_vocproc_cmd(struct voice_data *v)
+{
+ struct apr_hdr cvp_disable_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvp = v->apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr regist failed\n", __func__);
+ return -EINVAL;
+ }
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* disable vocproc and wait for respose */
+ cvp_disable_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_disable_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_disable_cmd) - APR_HDR_SIZE);
+ pr_debug("cvp_disable_cmd pkt size = %d, cvp_handle=%d\n",
+ cvp_disable_cmd.pkt_size, cvp_handle);
+ cvp_disable_cmd.src_port = 0;
+ cvp_disable_cmd.dest_port = cvp_handle;
+ cvp_disable_cmd.token = 0;
+ cvp_disable_cmd.opcode = VSS_IVOCPROC_CMD_DISABLE;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_disable_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IVOCPROC_CMD_DISABLE\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_set_device_cmd(struct voice_data *v)
+{
+ struct cvp_set_device_cmd cvp_setdev_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvp = v->apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* set device and wait for response */
+ cvp_setdev_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_setdev_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_setdev_cmd) - APR_HDR_SIZE);
+ pr_debug(" send create cvp setdev, pkt size = %d\n",
+ cvp_setdev_cmd.hdr.pkt_size);
+ cvp_setdev_cmd.hdr.src_port = 0;
+ cvp_setdev_cmd.hdr.dest_port = cvp_handle;
+ cvp_setdev_cmd.hdr.token = 0;
+ cvp_setdev_cmd.hdr.opcode = VSS_IVOCPROC_CMD_SET_DEVICE;
+
+ /* Use default topology if invalid value in ACDB */
+ cvp_setdev_cmd.cvp_set_device.tx_topology_id =
+ get_voice_tx_topology();
+ if (cvp_setdev_cmd.cvp_set_device.tx_topology_id == 0)
+ cvp_setdev_cmd.cvp_set_device.tx_topology_id =
+ VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS;
+
+ cvp_setdev_cmd.cvp_set_device.rx_topology_id =
+ get_voice_rx_topology();
+ if (cvp_setdev_cmd.cvp_set_device.rx_topology_id == 0)
+ cvp_setdev_cmd.cvp_set_device.rx_topology_id =
+ VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT;
+ cvp_setdev_cmd.cvp_set_device.tx_port_id = v->dev_tx.port_id;
+ cvp_setdev_cmd.cvp_set_device.rx_port_id = v->dev_rx.port_id;
+ pr_debug("topology=%d , tx_port_id=%d, rx_port_id=%d\n",
+ cvp_setdev_cmd.cvp_set_device.tx_topology_id,
+ cvp_setdev_cmd.cvp_set_device.tx_port_id,
+ cvp_setdev_cmd.cvp_set_device.rx_port_id);
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_setdev_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VOCPROC_FULL_CONTROL_SESSION\n");
+ goto fail;
+ }
+ pr_debug("wait for cvp create session event\n");
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_stop_voice_cmd(struct voice_data *v)
+{
+ struct apr_hdr mvm_stop_voice_cmd;
+ int ret = 0;
+ void *apr_mvm;
+ u16 mvm_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = v->apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ mvm_stop_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_stop_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_stop_voice_cmd) - APR_HDR_SIZE);
+ pr_debug("send mvm_stop_voice_cmd pkt size = %d\n",
+ mvm_stop_voice_cmd.pkt_size);
+ mvm_stop_voice_cmd.src_port = 0;
+ mvm_stop_voice_cmd.dest_port = mvm_handle;
+ mvm_stop_voice_cmd.token = 0;
+ mvm_stop_voice_cmd.opcode = VSS_IMVM_CMD_STOP_VOICE;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_stop_voice_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IMVM_CMD_STOP_VOICE\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static int voice_setup_vocproc(struct voice_data *v)
+{
+ struct cvp_create_full_ctl_session_cmd cvp_session_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvp = v->apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ /* create cvp session and wait for response */
+ cvp_session_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_session_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_session_cmd) - APR_HDR_SIZE);
+ pr_debug(" send create cvp session, pkt size = %d\n",
+ cvp_session_cmd.hdr.pkt_size);
+ cvp_session_cmd.hdr.src_port = 0;
+ cvp_session_cmd.hdr.dest_port = 0;
+ cvp_session_cmd.hdr.token = 0;
+ cvp_session_cmd.hdr.opcode =
+ VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION;
+
+ /* Use default topology if invalid value in ACDB */
+ cvp_session_cmd.cvp_session.tx_topology_id =
+ get_voice_tx_topology();
+ if (cvp_session_cmd.cvp_session.tx_topology_id == 0)
+ cvp_session_cmd.cvp_session.tx_topology_id =
+ VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS;
+
+ cvp_session_cmd.cvp_session.rx_topology_id =
+ get_voice_rx_topology();
+ if (cvp_session_cmd.cvp_session.rx_topology_id == 0)
+ cvp_session_cmd.cvp_session.rx_topology_id =
+ VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT;
+
+ cvp_session_cmd.cvp_session.direction = 2; /*tx and rx*/
+ cvp_session_cmd.cvp_session.network_id = VSS_NETWORK_ID_DEFAULT;
+ cvp_session_cmd.cvp_session.tx_port_id = v->dev_tx.port_id;
+ cvp_session_cmd.cvp_session.rx_port_id = v->dev_rx.port_id;
+
+ pr_debug("topology=%d net_id=%d, dir=%d tx_port_id=%d, rx_port_id=%d\n",
+ cvp_session_cmd.cvp_session.tx_topology_id,
+ cvp_session_cmd.cvp_session.network_id,
+ cvp_session_cmd.cvp_session.direction,
+ cvp_session_cmd.cvp_session.tx_port_id,
+ cvp_session_cmd.cvp_session.rx_port_id);
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_session_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VOCPROC_FULL_CONTROL_SESSION\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+
+ /* enable vocproc */
+ ret = voice_send_enable_vocproc_cmd(v);
+ if (ret < 0)
+ goto fail;
+
+ /* attach vocproc */
+ ret = voice_send_attach_vocproc_cmd(v);
+ if (ret < 0)
+ goto fail;
+
+ /* send tty mode if tty device is used */
+ voice_send_tty_mode_cmd(v);
+
+ if (v->voc_path == VOC_PATH_FULL)
+ voice_send_netid_timing_cmd(v);
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_enable_vocproc_cmd(struct voice_data *v)
+{
+ int ret = 0;
+ struct apr_hdr cvp_enable_cmd;
+ void *apr_cvp;
+ u16 cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvp = v->apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* enable vocproc and wait for respose */
+ cvp_enable_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_enable_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_enable_cmd) - APR_HDR_SIZE);
+ pr_debug("cvp_enable_cmd pkt size = %d, cvp_handle=%d\n",
+ cvp_enable_cmd.pkt_size, cvp_handle);
+ cvp_enable_cmd.src_port = 0;
+ cvp_enable_cmd.dest_port = cvp_handle;
+ cvp_enable_cmd.token = 0;
+ cvp_enable_cmd.opcode = VSS_IVOCPROC_CMD_ENABLE;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_enable_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_IVOCPROC_CMD_ENABLE\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_netid_timing_cmd(struct voice_data *v)
+{
+ int ret = 0;
+ void *apr_mvm;
+ u16 mvm_handle;
+ struct mvm_set_network_cmd mvm_set_network;
+ struct mvm_set_voice_timing_cmd mvm_set_voice_timing;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = v->apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+
+ ret = voice_config_cvs_vocoder(v);
+ if (ret < 0) {
+ pr_err("%s: Error %d configuring CVS voc",
+ __func__, ret);
+ goto fail;
+ }
+ /* Set network ID. */
+ pr_debug("Setting network ID\n");
+
+ mvm_set_network.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_set_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_set_network) - APR_HDR_SIZE);
+ mvm_set_network.hdr.src_port = 0;
+ mvm_set_network.hdr.dest_port = mvm_handle;
+ mvm_set_network.hdr.token = 0;
+ mvm_set_network.hdr.opcode = VSS_ICOMMON_CMD_SET_NETWORK;
+ mvm_set_network.network.network_id = v->mvs_info.network_type;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_network);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_NETWORK\n", __func__, ret);
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+
+ /* Set voice timing. */
+ pr_debug("Setting voice timing\n");
+
+ mvm_set_voice_timing.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_set_voice_timing.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_set_voice_timing) -
+ APR_HDR_SIZE);
+ mvm_set_voice_timing.hdr.src_port = 0;
+ mvm_set_voice_timing.hdr.dest_port = mvm_handle;
+ mvm_set_voice_timing.hdr.token = 0;
+ mvm_set_voice_timing.hdr.opcode = VSS_ICOMMON_CMD_SET_VOICE_TIMING;
+ mvm_set_voice_timing.timing.mode = 0;
+ mvm_set_voice_timing.timing.enc_offset = 8000;
+ mvm_set_voice_timing.timing.dec_req_offset = 3300;
+ mvm_set_voice_timing.timing.dec_offset = 8300;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_voice_timing);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending SET_TIMING\n", __func__, ret);
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_attach_vocproc_cmd(struct voice_data *v)
+{
+ int ret = 0;
+ struct mvm_attach_vocproc_cmd mvm_a_vocproc_cmd;
+ void *apr_mvm;
+ u16 mvm_handle, cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = v->apr_q6_mvm;
+
+ if (!apr_mvm) {
+ pr_err("%s: apr_mvm is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* attach vocproc and wait for response */
+ mvm_a_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_a_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_a_vocproc_cmd) - APR_HDR_SIZE);
+ pr_debug("send mvm_a_vocproc_cmd pkt size = %d\n",
+ mvm_a_vocproc_cmd.hdr.pkt_size);
+ mvm_a_vocproc_cmd.hdr.src_port = 0;
+ mvm_a_vocproc_cmd.hdr.dest_port = mvm_handle;
+ mvm_a_vocproc_cmd.hdr.token = 0;
+ mvm_a_vocproc_cmd.hdr.opcode = VSS_ISTREAM_CMD_ATTACH_VOCPROC;
+ mvm_a_vocproc_cmd.mvm_attach_cvp_handle.handle = cvp_handle;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_a_vocproc_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_ISTREAM_CMD_ATTACH_VOCPROC\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static int voice_destroy_vocproc(struct voice_data *v)
+{
+ struct mvm_detach_vocproc_cmd mvm_d_vocproc_cmd;
+ struct apr_hdr cvp_destroy_session_cmd;
+ int ret = 0;
+ void *apr_mvm, *apr_cvp;
+ u16 mvm_handle, cvp_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_mvm = v->apr_q6_mvm;
+ apr_cvp = v->apr_q6_cvp;
+
+ if (!apr_mvm || !apr_cvp) {
+ pr_err("%s: apr_mvm or apr_cvp is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ mvm_handle = voice_get_mvm_handle(v);
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* send stop voice cmd */
+ voice_send_stop_voice_cmd(v);
+
+ /* detach VOCPROC and wait for response from mvm */
+ mvm_d_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ mvm_d_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(mvm_d_vocproc_cmd) - APR_HDR_SIZE);
+ pr_debug("mvm_d_vocproc_cmd pkt size = %d\n",
+ mvm_d_vocproc_cmd.hdr.pkt_size);
+ mvm_d_vocproc_cmd.hdr.src_port = 0;
+ mvm_d_vocproc_cmd.hdr.dest_port = mvm_handle;
+ mvm_d_vocproc_cmd.hdr.token = 0;
+ mvm_d_vocproc_cmd.hdr.opcode = VSS_ISTREAM_CMD_DETACH_VOCPROC;
+ mvm_d_vocproc_cmd.mvm_detach_cvp_handle.handle = cvp_handle;
+
+ v->mvm_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_d_vocproc_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending VSS_ISTREAM_CMD_DETACH_VOCPROC\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->mvm_wait,
+ (v->mvm_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+
+ /* destrop cvp session */
+ cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_destroy_session_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_destroy_session_cmd) - APR_HDR_SIZE);
+ pr_debug("cvp_destroy_session_cmd pkt size = %d\n",
+ cvp_destroy_session_cmd.pkt_size);
+ cvp_destroy_session_cmd.src_port = 0;
+ cvp_destroy_session_cmd.dest_port = cvp_handle;
+ cvp_destroy_session_cmd.token = 0;
+ cvp_destroy_session_cmd.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
+
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_destroy_session_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending APRV2_IBASIC_CMD_DESTROY_SESSION\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ goto fail;
+ }
+
+ cvp_handle = 0;
+ voice_set_cvp_handle(v, cvp_handle);
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_mute_cmd(struct voice_data *v)
+{
+ struct cvs_set_mute_cmd cvs_mute_cmd;
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = v->apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ cvs_handle = voice_get_cvs_handle(v);
+
+ /* send mute/unmute to cvs */
+ cvs_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_mute_cmd) - APR_HDR_SIZE);
+ cvs_mute_cmd.hdr.src_port = 0;
+ cvs_mute_cmd.hdr.dest_port = cvs_handle;
+ cvs_mute_cmd.hdr.token = 0;
+ cvs_mute_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MUTE;
+ cvs_mute_cmd.cvs_set_mute.direction = 0; /*tx*/
+ cvs_mute_cmd.cvs_set_mute.mute_flag = v->dev_tx.mute;
+
+ pr_info(" mute value =%d\n", cvs_mute_cmd.cvs_set_mute.mute_flag);
+ v->cvs_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_mute_cmd);
+ if (ret < 0) {
+ pr_err("Fail: send STREAM SET MUTE\n");
+ goto fail;
+ }
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret)
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static int voice_send_vol_index_cmd(struct voice_data *v)
+{
+ struct cvp_set_rx_volume_index_cmd cvp_vol_cmd;
+ int ret = 0;
+ void *apr_cvp;
+ u16 cvp_handle;
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvp = v->apr_q6_cvp;
+
+ if (!apr_cvp) {
+ pr_err("%s: apr_cvp is NULL.\n", __func__);
+ return -EINVAL;
+ }
+ cvp_handle = voice_get_cvp_handle(v);
+
+ /* send volume index to cvp */
+ cvp_vol_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvp_vol_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvp_vol_cmd) - APR_HDR_SIZE);
+ cvp_vol_cmd.hdr.src_port = 0;
+ cvp_vol_cmd.hdr.dest_port = cvp_handle;
+ cvp_vol_cmd.hdr.token = 0;
+ cvp_vol_cmd.hdr.opcode = VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX;
+ cvp_vol_cmd.cvp_set_vol_idx.vol_index = v->dev_rx.volume;
+ v->cvp_state = CMD_STATUS_FAIL;
+ ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_vol_cmd);
+ if (ret < 0) {
+ pr_err("Fail in sending RX VOL INDEX\n");
+ return -EINVAL;
+ }
+ ret = wait_event_timeout(v->cvp_wait,
+ (v->cvp_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int voc_disable_cvp(void)
+{
+ struct voice_data *v = &voice;
+ int ret = 0;
+
+ mutex_lock(&v->lock);
+
+ if (v->voc_state == VOC_RUN) {
+ afe_sidetone(v->dev_tx.port_id, v->dev_rx.port_id, 0, 0);
+ /* send cmd to dsp to disable vocproc */
+ ret = voice_send_disable_vocproc_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: disable vocproc failed\n", __func__);
+ goto fail;
+ }
+ v->voc_state = VOC_CHANGE;
+ }
+
+fail: mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_enable_cvp(void)
+{
+ struct voice_data *v = &voice;
+ int ret = 0;
+
+ mutex_lock(&v->lock);
+
+ if (v->voc_state == VOC_CHANGE) {
+ ret = voice_send_set_device_cmd(v);
+ if (ret < 0) {
+ pr_err("%s: set device failed\n", __func__);
+ goto fail;
+ }
+ ret = voice_send_enable_vocproc_cmd(v);
+ if (ret < 0) {
+ pr_err("enable vocproc failed\n");
+ goto fail;
+ }
+ ret = afe_sidetone(v->dev_tx.port_id, v->dev_rx.port_id,
+ 1, v->sidetone_gain);
+
+ if (ret < 0)
+ pr_err("AFE command sidetone failed\n");
+
+ v->voc_state = VOC_RUN;
+ }
+
+fail:
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_set_tx_mute(uint32_t dir, uint32_t mute)
+{
+ struct voice_data *v = &voice;
+ int ret = 0;
+
+ mutex_lock(&v->lock);
+
+ v->dev_tx.mute = mute;
+
+ if (v->voc_state == VOC_RUN)
+ ret = voice_send_mute_cmd(v);
+
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_set_rx_vol_index(uint32_t dir, uint32_t vol_idx)
+{
+ struct voice_data *v = &voice;
+ int ret = 0;
+
+ mutex_lock(&v->lock);
+
+ v->dev_rx.volume = vol_idx;
+
+ if (v->voc_state == VOC_RUN)
+ ret = voice_send_vol_index_cmd(v);
+
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+void voc_set_rxtx_port(uint32_t port_id, uint32_t dev_type)
+{
+ struct voice_data *v = &voice;
+
+ pr_debug(" port_id=%d, type=%d\n", port_id, dev_type);
+
+ mutex_lock(&v->lock);
+
+ if (dev_type == DEV_RX)
+ v->dev_rx.port_id = port_id;
+ else
+ v->dev_tx.port_id = port_id;
+
+ mutex_unlock(&v->lock);
+
+ return;
+}
+
+void voc_set_route_flag(uint8_t path_dir, uint8_t set)
+{
+ struct voice_data *v = &voice;
+
+ pr_debug("path_dir=%d, set=%d\n", path_dir, set);
+
+ mutex_lock(&v->lock);
+
+ if (path_dir == RX_PATH)
+ v->voc_route_state.rx_route_flag = set;
+ else
+ v->voc_route_state.tx_route_flag = set;
+
+ mutex_unlock(&v->lock);
+
+ return;
+}
+
+uint8_t voc_get_route_flag(uint8_t path_dir)
+{
+ struct voice_data *v = &voice;
+ int ret = 0;
+
+ mutex_lock(&v->lock);
+
+ if (path_dir == RX_PATH)
+ ret = v->voc_route_state.rx_route_flag;
+ else
+ ret = v->voc_route_state.tx_route_flag;
+
+ mutex_unlock(&v->lock);
+
+ return ret;
+}
+
+int voc_end_voice_call(void)
+{
+ struct voice_data *v = &voice;
+ int ret = 0;
+
+ mutex_lock(&v->lock);
+
+ if (v->voc_state == VOC_RUN) {
+ ret = voice_destroy_vocproc(v);
+ if (ret < 0)
+ pr_err("%s: destroy voice failed\n", __func__);
+ voice_destroy_mvm_cvs_session(v);
+
+ v->voc_state = VOC_RELEASE;
+ }
+ mutex_unlock(&v->lock);
+ return ret;
+}
+
+int voc_start_voice_call(void)
+{
+ struct voice_data *v = &voice;
+ int ret = 0;
+
+ mutex_lock(&v->lock);
+
+ if ((v->voc_state == VOC_INIT) ||
+ (v->voc_state == VOC_RELEASE)) {
+ ret = voice_apr_register(v);
+ if (ret < 0) {
+ pr_err("%s: apr register failed\n", __func__);
+ goto fail;
+ }
+ ret = voice_create_mvm_cvs_session(v);
+ if (ret < 0) {
+ pr_err("create mvm and cvs failed\n");
+ goto fail;
+ }
+ ret = voice_setup_vocproc(v);
+ if (ret < 0) {
+ pr_err("setup voice failed\n");
+ goto fail;
+ }
+ ret = voice_send_start_voice_cmd(v);
+ if (ret < 0) {
+ pr_err("start voice failed\n");
+ goto fail;
+ }
+ ret = afe_sidetone(v->dev_tx.port_id,
+ v->dev_rx.port_id, 1, v->sidetone_gain);
+ if (ret < 0)
+ pr_err("AFE command sidetone failed\n");
+
+ v->voc_state = VOC_RUN;
+ }
+
+fail: mutex_unlock(&v->lock);
+ return ret;
+}
+
+int voc_set_voc_path_full(uint32_t set)
+{
+ int rc = 0;
+
+ pr_debug("set voc path: %d\n", set);
+
+ mutex_lock(&voice.lock);
+
+ if (voice.voc_state == VOC_INIT || voice.voc_state == VOC_RELEASE) {
+ if (set)
+ voice.voc_path = VOC_PATH_FULL;
+ else
+ voice.voc_path = VOC_PATH_PASSIVE;
+ } else {
+ pr_err("%s: Invalid voc path set to %d, in state %d\n",
+ __func__, set, voice.voc_state);
+ rc = -EPERM;
+ }
+
+ mutex_unlock(&voice.lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(voc_set_voc_path_full);
+
+void voc_register_mvs_cb(ul_cb_fn ul_cb,
+ dl_cb_fn dl_cb,
+ void *private_data)
+{
+ voice.mvs_info.ul_cb = ul_cb;
+ voice.mvs_info.dl_cb = dl_cb;
+ voice.mvs_info.private_data = private_data;
+}
+
+void voc_config_vocoder(uint32_t media_type,
+ uint32_t rate,
+ uint32_t network_type)
+{
+ voice.mvs_info.media_type = media_type;
+ voice.mvs_info.rate = rate;
+ voice.mvs_info.network_type = network_type;
+}
+
+static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv)
+{
+ uint32_t *ptr;
+ struct voice_data *v;
+
+ if ((data == NULL) || (priv == NULL)) {
+ pr_err("%s: data or priv is NULL\n", __func__);
+ return -EINVAL;
+ }
+ v = priv;
+
+ pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
+ data->payload_size, data->opcode);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ if (data->payload_size) {
+ ptr = data->payload;
+
+ pr_info("%x %x\n", ptr[0], ptr[1]);
+ /* ping mvm service ACK */
+ switch (ptr[0]) {
+ case VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION:
+ case VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION:
+ /* Passive session is used for CS call
+ * Full session is used for VoIP call. */
+ pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+ if (!ptr[1]) {
+ pr_debug("%s: MVM handle is %d\n",
+ __func__, data->src_port);
+ voice_set_mvm_handle(v, data->src_port);
+ } else
+ pr_err("got NACK for sending \
+ MVM create session \n");
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->mvm_wait);
+ break;
+ case VSS_IMVM_CMD_START_VOICE:
+ case VSS_ISTREAM_CMD_ATTACH_VOCPROC:
+ case VSS_IMVM_CMD_STOP_VOICE:
+ case VSS_ISTREAM_CMD_DETACH_VOCPROC:
+ case VSS_ISTREAM_CMD_SET_TTY_MODE:
+ case APRV2_IBASIC_CMD_DESTROY_SESSION:
+ case VSS_IMVM_CMD_ATTACH_STREAM:
+ case VSS_IMVM_CMD_DETACH_STREAM:
+ case VSS_ICOMMON_CMD_SET_NETWORK:
+ case VSS_ICOMMON_CMD_SET_VOICE_TIMING:
+ pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+ v->mvm_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->mvm_wait);
+ break;
+ default:
+ pr_debug("%s: not match cmd = 0x%x\n",
+ __func__, ptr[0]);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv)
+{
+ uint32_t *ptr;
+ struct voice_data *v;
+
+ if ((data == NULL) || (priv == NULL)) {
+ pr_err("%s: data or priv is NULL\n", __func__);
+ return -EINVAL;
+ }
+ v = priv;
+
+ pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
+ data->payload_size, data->opcode);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ if (data->payload_size) {
+ ptr = data->payload;
+
+ pr_info("%x %x\n", ptr[0], ptr[1]);
+ /*response from CVS */
+ switch (ptr[0]) {
+ case VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION:
+ case VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION:
+ if (!ptr[1]) {
+ pr_debug("%s: CVS handle is %d\n",
+ __func__, data->src_port);
+ voice_set_cvs_handle(v, data->src_port);
+ } else
+ pr_err("got NACK for sending \
+ CVS create session \n");
+ v->cvs_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->cvs_wait);
+ break;
+ case VSS_ISTREAM_CMD_SET_MUTE:
+ case VSS_ISTREAM_CMD_SET_MEDIA_TYPE:
+ case VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE:
+ case VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE:
+ case VSS_ISTREAM_CMD_SET_ENC_DTX_MODE:
+ case VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE:
+ case APRV2_IBASIC_CMD_DESTROY_SESSION:
+ pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+ v->cvs_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->cvs_wait);
+ break;
+ default:
+ pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+ break;
+ }
+ }
+ } else if (data->opcode == VSS_ISTREAM_EVT_SEND_ENC_BUFFER) {
+ uint32_t *voc_pkt = data->payload;
+ uint32_t pkt_len = data->payload_size;
+
+ if (voc_pkt != NULL && v->mvs_info.ul_cb != NULL) {
+ pr_debug("%s: Media type is 0x%x\n",
+ __func__, voc_pkt[0]);
+
+ /* Remove media ID from payload. */
+ voc_pkt++;
+ pkt_len = pkt_len - 4;
+
+ v->mvs_info.ul_cb((uint8_t *)voc_pkt,
+ pkt_len,
+ v->mvs_info.private_data);
+ } else
+ pr_err("%s: voc_pkt is 0x%x ul_cb is 0x%x\n",
+ __func__, (unsigned int)voc_pkt,
+ (unsigned int) v->mvs_info.ul_cb);
+ } else if (data->opcode == VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER) {
+ struct cvs_send_dec_buf_cmd send_dec_buf;
+ int ret = 0;
+ uint32_t pkt_len = 0;
+
+ if (v->mvs_info.dl_cb != NULL) {
+ send_dec_buf.dec_buf.media_id = v->mvs_info.media_type;
+
+ v->mvs_info.dl_cb(
+ (uint8_t *)&send_dec_buf.dec_buf.packet_data,
+ &pkt_len,
+ v->mvs_info.private_data);
+
+ send_dec_buf.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ send_dec_buf.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(send_dec_buf.dec_buf.media_id) + pkt_len);
+ send_dec_buf.hdr.src_port = 0;
+ send_dec_buf.hdr.dest_port = voice_get_cvs_handle(v);
+ send_dec_buf.hdr.token = 0;
+ send_dec_buf.hdr.opcode =
+ VSS_ISTREAM_EVT_SEND_DEC_BUFFER;
+
+ ret = apr_send_pkt(v->apr_q6_cvs,
+ (uint32_t *) &send_dec_buf);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending DEC_BUF\n",
+ __func__, ret);
+ goto fail;
+ }
+ } else
+ pr_debug("%s: dl_cb is NULL\n", __func__);
+ } else if (data->opcode == VSS_ISTREAM_EVT_SEND_DEC_BUFFER)
+ pr_debug("Send dec buf resp\n");
+
+ else
+ pr_debug("Unknown opcode 0x%x\n", data->opcode);
+
+fail:
+ return 0;
+}
+
+static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv)
+{
+ uint32_t *ptr;
+ struct voice_data *v;
+
+ if ((data == NULL) || (priv == NULL)) {
+ pr_err("%s: data or priv is NULL\n", __func__);
+ return -EINVAL;
+ }
+ v = priv;
+
+ pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
+ data->payload_size, data->opcode);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ if (data->payload_size) {
+ ptr = data->payload;
+
+ pr_info("%x %x\n", ptr[0], ptr[1]);
+
+ switch (ptr[0]) {
+ case VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION:
+ /*response from CVP */
+ pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+ if (!ptr[1]) {
+ voice_set_cvp_handle(v, data->src_port);
+ pr_debug("cvphdl=%d\n", data->src_port);
+ } else
+ pr_err("got NACK from CVP create \
+ session response\n");
+ v->cvp_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->cvp_wait);
+ break;
+ case VSS_IVOCPROC_CMD_SET_DEVICE:
+ case VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX:
+ case VSS_IVOCPROC_CMD_ENABLE:
+ case VSS_IVOCPROC_CMD_DISABLE:
+ case APRV2_IBASIC_CMD_DESTROY_SESSION:
+ v->cvp_state = CMD_STATUS_SUCCESS;
+ wake_up(&v->cvp_wait);
+ break;
+ default:
+ pr_debug("%s: not match cmd = 0x%x\n",
+ __func__, ptr[0]);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int __init voice_init(void)
+{
+ int rc = 0;
+ struct voice_data *v = &voice;
+
+ /* set default value */
+ v->default_mute_val = 1; /* default is mute */
+ v->default_vol_val = 0;
+ v->default_sample_val = 8000;
+ v->sidetone_gain = 0x512;
+
+ /* initialize dev_rx and dev_tx */
+ memset(&v->dev_tx, 0, sizeof(struct device_data));
+ memset(&v->dev_rx, 0, sizeof(struct device_data));
+ v->dev_rx.volume = v->default_vol_val;
+ v->dev_tx.mute = v->default_mute_val;
+
+ v->dev_tx.port_id = 1;
+ v->dev_rx.port_id = 0;
+
+ v->voc_state = VOC_INIT;
+ v->voc_path = VOC_PATH_PASSIVE;
+ init_waitqueue_head(&v->mvm_wait);
+ init_waitqueue_head(&v->cvs_wait);
+ init_waitqueue_head(&v->cvp_wait);
+
+ mutex_init(&v->lock);
+
+ v->mvm_full_handle = 0;
+ v->mvm_passive_handle = 0;
+ v->cvs_full_handle = 0;
+ v->cvs_passive_handle = 0;
+ v->cvp_full_handle = 0;
+ v->cvp_passive_handle = 0;
+
+ v->apr_q6_mvm = NULL;
+ v->apr_q6_cvs = NULL;
+ v->apr_q6_cvp = NULL;
+
+ /* Initialize MVS info. */
+ memset(&v->mvs_info, 0, sizeof(v->mvs_info));
+ v->mvs_info.network_type = VSS_NETWORK_ID_DEFAULT;
+
+ return rc;
+}
+
+device_initcall(voice_init);
diff --git a/sound/soc/msm/qdsp6/q6voice.h b/sound/soc/msm/qdsp6/q6voice.h
new file mode 100644
index 0000000..f9e1bc4
--- /dev/null
+++ b/sound/soc/msm/qdsp6/q6voice.h
@@ -0,0 +1,686 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+#ifndef __QDSP6VOICE_H__
+#define __QDSP6VOICE_H__
+
+#include <mach/qdsp6v2/apr.h>
+
+#define MAX_VOC_PKT_SIZE 322
+
+struct voice_header {
+ uint32_t id;
+ uint32_t data_len;
+};
+
+struct voice_init {
+ struct voice_header hdr;
+ void *cb_handle;
+};
+
+/* Device information payload structure */
+
+struct device_data {
+ uint32_t volume; /* in index */
+ uint32_t mute;
+ uint32_t sample;
+ uint32_t enabled;
+ uint32_t dev_id;
+ uint32_t port_id;
+};
+
+struct voice_dev_route_state {
+ u16 rx_route_flag;
+ u16 tx_route_flag;
+};
+
+enum {
+ VOC_INIT = 0,
+ VOC_RUN,
+ VOC_CHANGE,
+ VOC_RELEASE,
+};
+
+/* TO MVM commands */
+#define VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x000110FF
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110FE
+/* Create a new full control MVM session. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_ATTACH_STREAM 0x0001123C
+/* Attach a stream to the MVM. */
+
+#define VSS_IMVM_CMD_DETACH_STREAM 0x0001123D
+/* Detach a stream from the MVM. */
+
+#define VSS_IMVM_CMD_START_VOICE 0x00011190
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_STOP_VOICE 0x00011192
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_ATTACH_VOCPROC 0x000110F8
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_DETACH_VOCPROC 0x000110F9
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+
+#define VSS_ISTREAM_CMD_SET_TTY_MODE 0x00011196
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ICOMMON_CMD_SET_NETWORK 0x0001119C
+/* Set the network type. */
+
+#define VSS_ICOMMON_CMD_SET_VOICE_TIMING 0x000111E0
+/* Set the voice timing parameters. */
+
+struct vss_istream_cmd_set_tty_mode_t {
+ uint32_t mode;
+ /**<
+ * TTY mode.
+ *
+ * 0 : TTY disabled
+ * 1 : HCO
+ * 2 : VCO
+ * 3 : FULL
+ */
+} __packed;
+
+struct vss_istream_cmd_attach_vocproc_t {
+ uint16_t handle;
+ /**< Handle of vocproc being attached. */
+} __packed;
+
+struct vss_istream_cmd_detach_vocproc_t {
+ uint16_t handle;
+ /**< Handle of vocproc being detached. */
+} __packed;
+
+struct vss_imvm_cmd_attach_stream_t {
+ uint16_t handle;
+ /* The stream handle to attach. */
+} __packed;
+
+struct vss_imvm_cmd_detach_stream_t {
+ uint16_t handle;
+ /* The stream handle to detach. */
+} __packed;
+
+struct vss_icommon_cmd_set_network_t {
+ uint32_t network_id;
+ /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */
+} __packed;
+
+struct vss_icommon_cmd_set_voice_timing_t {
+ uint16_t mode;
+ /*
+ * The vocoder frame synchronization mode.
+ *
+ * 0 : No frame sync.
+ * 1 : Hard VFR (20ms Vocoder Frame Reference interrupt).
+ */
+ uint16_t enc_offset;
+ /*
+ * The offset in microseconds from the VFR to deliver a Tx vocoder
+ * packet. The offset should be less than 20000us.
+ */
+ uint16_t dec_req_offset;
+ /*
+ * The offset in microseconds from the VFR to request for an Rx vocoder
+ * packet. The offset should be less than 20000us.
+ */
+ uint16_t dec_offset;
+ /*
+ * The offset in microseconds from the VFR to indicate the deadline to
+ * receive an Rx vocoder packet. The offset should be less than 20000us.
+ * Rx vocoder packets received after this deadline are not guaranteed to
+ * be processed.
+ */
+} __packed;
+
+struct vss_imvm_cmd_create_full_control_session_t {
+ char name[20];
+ /*
+ * A variable-sized stream name.
+ *
+ * The stream name size is the payload size minus the size of the other
+ * fields.
+ */
+} __packed;
+
+
+struct mvm_attach_vocproc_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_attach_vocproc_t mvm_attach_cvp_handle;
+} __packed;
+
+struct mvm_detach_vocproc_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_detach_vocproc_t mvm_detach_cvp_handle;
+} __packed;
+
+struct mvm_create_passive_ctl_session_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct mvm_create_full_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_imvm_cmd_create_full_control_session_t mvm_session;
+} __packed;
+
+struct mvm_set_tty_mode_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_tty_mode_t tty_mode;
+} __packed;
+
+struct mvm_attach_stream_cmd {
+ struct apr_hdr hdr;
+ struct vss_imvm_cmd_attach_stream_t attach_stream;
+} __packed;
+
+struct mvm_detach_stream_cmd {
+ struct apr_hdr hdr;
+ struct vss_imvm_cmd_detach_stream_t detach_stream;
+} __packed;
+
+struct mvm_set_network_cmd {
+ struct apr_hdr hdr;
+ struct vss_icommon_cmd_set_network_t network;
+} __packed;
+
+struct mvm_set_voice_timing_cmd {
+ struct apr_hdr hdr;
+ struct vss_icommon_cmd_set_voice_timing_t timing;
+} __packed;
+
+/* TO CVS commands */
+#define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x00011140
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION 0x000110F7
+/* Create a new full control stream session. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
+
+#define VSS_ISTREAM_CMD_SET_MUTE 0x00011022
+
+#define VSS_ISTREAM_CMD_SET_MEDIA_TYPE 0x00011186
+/* Set media type on the stream. */
+
+#define VSS_ISTREAM_EVT_SEND_ENC_BUFFER 0x00011015
+/* Event sent by the stream to its client to provide an encoded packet. */
+
+#define VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER 0x00011017
+/* Event sent by the stream to its client requesting for a decoder packet.
+ * The client should respond with a VSS_ISTREAM_EVT_SEND_DEC_BUFFER event.
+ */
+
+#define VSS_ISTREAM_EVT_SEND_DEC_BUFFER 0x00011016
+/* Event sent by the client to the stream in response to a
+ * VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER event, providing a decoder packet.
+ */
+
+#define VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE 0x0001113E
+/* Set AMR encoder rate. */
+
+#define VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE 0x0001113F
+/* Set AMR-WB encoder rate. */
+
+#define VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE 0x00011019
+/* Set encoder minimum and maximum rate. */
+
+#define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE 0x0001101D
+/* Set encoder DTX mode. */
+
+struct vss_istream_cmd_create_passive_control_session_t {
+ char name[20];
+ /**<
+ * A variable-sized stream name.
+ *
+ * The stream name size is the payload size minus the size of the other
+ * fields.
+ */
+} __packed;
+
+struct vss_istream_cmd_set_mute_t {
+ uint16_t direction;
+ /**<
+ * 0 : TX only
+ * 1 : RX only
+ * 2 : TX and Rx
+ */
+ uint16_t mute_flag;
+ /**<
+ * Mute, un-mute.
+ *
+ * 0 : Silence disable
+ * 1 : Silence enable
+ * 2 : CNG enable. Applicable to TX only. If set on RX behavior
+ * will be the same as 1
+ */
+} __packed;
+
+struct vss_istream_cmd_create_full_control_session_t {
+ uint16_t direction;
+ /*
+ * Stream direction.
+ *
+ * 0 : TX only
+ * 1 : RX only
+ * 2 : TX and RX
+ * 3 : TX and RX loopback
+ */
+ uint32_t enc_media_type;
+ /* Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+ uint32_t dec_media_type;
+ /* Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+ uint32_t network_id;
+ /* Network ID. (Refer to VSS_NETWORK_ID_XXX). */
+ char name[20];
+ /*
+ * A variable-sized stream name.
+ *
+ * The stream name size is the payload size minus the size of the other
+ * fields.
+ */
+} __packed;
+
+struct vss_istream_cmd_set_media_type_t {
+ uint32_t rx_media_id;
+ /* Set the Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+ uint32_t tx_media_id;
+ /* Set the Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+} __packed;
+
+struct vss_istream_evt_send_enc_buffer_t {
+ uint32_t media_id;
+ /* Media ID of the packet. */
+ uint8_t packet_data[MAX_VOC_PKT_SIZE];
+ /* Packet data buffer. */
+} __packed;
+
+struct vss_istream_evt_send_dec_buffer_t {
+ uint32_t media_id;
+ /* Media ID of the packet. */
+ uint8_t packet_data[MAX_VOC_PKT_SIZE];
+ /* Packet data. */
+} __packed;
+
+struct vss_istream_cmd_voc_amr_set_enc_rate_t {
+ uint32_t mode;
+ /* Set the AMR encoder rate.
+ *
+ * 0x00000000 : 4.75 kbps
+ * 0x00000001 : 5.15 kbps
+ * 0x00000002 : 5.90 kbps
+ * 0x00000003 : 6.70 kbps
+ * 0x00000004 : 7.40 kbps
+ * 0x00000005 : 7.95 kbps
+ * 0x00000006 : 10.2 kbps
+ * 0x00000007 : 12.2 kbps
+ */
+} __packed;
+
+struct vss_istream_cmd_voc_amrwb_set_enc_rate_t {
+ uint32_t mode;
+ /* Set the AMR-WB encoder rate.
+ *
+ * 0x00000000 : 6.60 kbps
+ * 0x00000001 : 8.85 kbps
+ * 0x00000002 : 12.65 kbps
+ * 0x00000003 : 14.25 kbps
+ * 0x00000004 : 15.85 kbps
+ * 0x00000005 : 18.25 kbps
+ * 0x00000006 : 19.85 kbps
+ * 0x00000007 : 23.05 kbps
+ * 0x00000008 : 23.85 kbps
+ */
+} __packed;
+
+struct vss_istream_cmd_cdma_set_enc_minmax_rate_t {
+ uint16_t min_rate;
+ /* Set the lower bound encoder rate.
+ *
+ * 0x0000 : Blank frame
+ * 0x0001 : Eighth rate
+ * 0x0002 : Quarter rate
+ * 0x0003 : Half rate
+ * 0x0004 : Full rate
+ */
+ uint16_t max_rate;
+ /* Set the upper bound encoder rate.
+ *
+ * 0x0000 : Blank frame
+ * 0x0001 : Eighth rate
+ * 0x0002 : Quarter rate
+ * 0x0003 : Half rate
+ * 0x0004 : Full rate
+ */
+} __packed;
+
+struct vss_istream_cmd_set_enc_dtx_mode_t {
+ uint32_t enable;
+ /* Toggle DTX on or off.
+ *
+ * 0 : Disables DTX
+ * 1 : Enables DTX
+ */
+} __packed;
+
+struct cvs_create_passive_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_create_passive_control_session_t cvs_session;
+} __packed;
+
+struct cvs_create_full_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_create_full_control_session_t cvs_session;
+};
+
+struct cvs_destroy_session_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvs_set_mute_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_mute_t cvs_set_mute;
+} __packed;
+
+struct cvs_set_media_type_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_media_type_t media_type;
+} __packed;
+
+struct cvs_send_dec_buf_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_evt_send_dec_buffer_t dec_buf;
+} __packed;
+
+struct cvs_set_amr_enc_rate_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_voc_amr_set_enc_rate_t amr_rate;
+} __packed;
+
+struct cvs_set_amrwb_enc_rate_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_voc_amrwb_set_enc_rate_t amrwb_rate;
+} __packed;
+
+struct cvs_set_cdma_enc_minmax_rate_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_cdma_set_enc_minmax_rate_t cdma_rate;
+} __packed;
+
+struct cvs_set_enc_dtx_mode_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_set_enc_dtx_mode_t dtx_mode;
+} __packed;
+
+/* TO CVP commands */
+
+#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION 0x000100C3
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION 0x0001003C
+
+#define VSS_IVOCPROC_CMD_SET_DEVICE 0x000100C4
+
+#define VSS_IVOCPROC_CMD_SET_VP3_DATA 0x000110EB
+
+#define VSS_IVOCPROC_CMD_SET_RX_VOLUME_INDEX 0x000110EE
+
+#define VSS_IVOCPROC_CMD_ENABLE 0x000100C6
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IVOCPROC_CMD_DISABLE 0x000110E1
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IVOCPROC_TOPOLOGY_ID_NONE 0x00010F70
+#define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS 0x00010F71
+#define VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE 0x00010F72
+
+#define VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT 0x00010F77
+
+/* Newtwork IDs */
+#define VSS_NETWORK_ID_DEFAULT 0x00010037
+#define VSS_NETWORK_ID_VOIP_NB 0x00011240
+#define VSS_NETWORK_ID_VOIP_WB 0x00011241
+#define VSS_NETWORK_ID_VOIP_WV 0x00011242
+
+/* Media types */
+#define VSS_MEDIA_ID_EVRC_MODEM 0x00010FC2
+/* 80-VF690-47 CDMA enhanced variable rate vocoder modem format. */
+#define VSS_MEDIA_ID_AMR_NB_MODEM 0x00010FC6
+/* 80-VF690-47 UMTS AMR-NB vocoder modem format. */
+#define VSS_MEDIA_ID_AMR_WB_MODEM 0x00010FC7
+/* 80-VF690-47 UMTS AMR-WB vocoder modem format. */
+#define VSS_MEDIA_ID_PCM_NB 0x00010FCB
+#define VSS_MEDIA_ID_PCM_WB 0x00010FCC
+/* Linear PCM (16-bit, little-endian). */
+#define VSS_MEDIA_ID_G711_ALAW 0x00010FCD
+/* G.711 a-law (contains two 10ms vocoder frames). */
+#define VSS_MEDIA_ID_G711_MULAW 0x00010FCE
+/* G.711 mu-law (contains two 10ms vocoder frames). */
+#define VSS_MEDIA_ID_G729 0x00010FD0
+/* G.729AB (contains two 10ms vocoder frames. */
+
+#define VOICE_CMD_SET_PARAM 0x00011006
+#define VOICE_CMD_GET_PARAM 0x00011007
+#define VOICE_EVT_GET_PARAM_ACK 0x00011008
+
+struct vss_ivocproc_cmd_create_full_control_session_t {
+ uint16_t direction;
+ /*
+ * stream direction.
+ * 0 : TX only
+ * 1 : RX only
+ * 2 : TX and RX
+ */
+ uint32_t tx_port_id;
+ /*
+ * TX device port ID which vocproc will connect to. If not supplying a
+ * port ID set to VSS_IVOCPROC_PORT_ID_NONE.
+ */
+ uint32_t tx_topology_id;
+ /*
+ * Tx leg topology ID. If not supplying a topology ID set to
+ * VSS_IVOCPROC_TOPOLOGY_ID_NONE.
+ */
+ uint32_t rx_port_id;
+ /*
+ * RX device port ID which vocproc will connect to. If not supplying a
+ * port ID set to VSS_IVOCPROC_PORT_ID_NONE.
+ */
+ uint32_t rx_topology_id;
+ /*
+ * Rx leg topology ID. If not supplying a topology ID set to
+ * VSS_IVOCPROC_TOPOLOGY_ID_NONE.
+ */
+ int32_t network_id;
+ /*
+ * Network ID. (Refer to VSS_NETWORK_ID_XXX). If not supplying a network
+ * ID set to VSS_NETWORK_ID_DEFAULT.
+ */
+} __packed;
+
+struct vss_ivocproc_cmd_set_volume_index_t {
+ uint16_t vol_index;
+ /**<
+ * Volume index utilized by the vocproc to index into the volume table
+ * provided in VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE and set
+ * volume on the VDSP.
+ */
+} __packed;
+
+struct vss_ivocproc_cmd_set_device_t {
+ uint32_t tx_port_id;
+ /**<
+ * TX device port ID which vocproc will connect to.
+ * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
+ */
+ uint32_t tx_topology_id;
+ /**<
+ * TX leg topology ID.
+ * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
+ * pre/post-processing blocks and is pass-through.
+ */
+ int32_t rx_port_id;
+ /**<
+ * RX device port ID which vocproc will connect to.
+ * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
+ */
+ uint32_t rx_topology_id;
+ /**<
+ * RX leg topology ID.
+ * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
+ * pre/post-processing blocks and is pass-through.
+ */
+} __packed;
+
+struct cvp_create_full_ctl_session_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_create_full_control_session_t cvp_session;
+} __packed;
+
+struct cvp_command {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvp_set_device_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_set_device_t cvp_set_device;
+} __packed;
+
+struct cvp_set_vp3_data_cmd {
+ struct apr_hdr hdr;
+} __packed;
+
+struct cvp_set_rx_volume_index_cmd {
+ struct apr_hdr hdr;
+ struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx;
+} __packed;
+
+/* CB for up-link packets. */
+typedef void (*ul_cb_fn)(uint8_t *voc_pkt,
+ uint32_t pkt_len,
+ void *private_data);
+
+/* CB for down-link packets. */
+typedef void (*dl_cb_fn)(uint8_t *voc_pkt,
+ uint32_t *pkt_len,
+ void *private_data);
+
+
+struct mvs_driver_info {
+ uint32_t media_type;
+ uint32_t rate;
+ uint32_t network_type;
+ ul_cb_fn ul_cb;
+ dl_cb_fn dl_cb;
+ void *private_data;
+};
+
+struct incall_rec_info {
+ uint32_t pending;
+ uint32_t rec_mode;
+};
+
+struct incall_music_info {
+ uint32_t pending;
+ uint32_t playing;
+};
+
+struct voice_data {
+ int voc_state;/*INIT, CHANGE, RELEASE, RUN */
+ uint32_t voc_path;
+
+ wait_queue_head_t mvm_wait;
+ wait_queue_head_t cvs_wait;
+ wait_queue_head_t cvp_wait;
+
+ uint32_t device_events;
+
+ /* cache the values related to Rx and Tx */
+ struct device_data dev_rx;
+ struct device_data dev_tx;
+
+ /* these default values are for all devices */
+ uint32_t default_mute_val;
+ uint32_t default_vol_val;
+ uint32_t default_sample_val;
+
+ /* APR to MVM in the Q6 */
+ void *apr_q6_mvm;
+ /* APR to CVS in the Q6 */
+ void *apr_q6_cvs;
+ /* APR to CVP in the Q6 */
+ void *apr_q6_cvp;
+
+ u32 mvm_state;
+ u32 cvs_state;
+ u32 cvp_state;
+
+ /* Handle to MVM in the Q6 */
+ u16 mvm_passive_handle; /* for cs call */
+ u16 mvm_full_handle; /* for voip */
+ /* Handle to CVS in the Q6 */
+ u16 cvs_passive_handle;
+ u16 cvs_full_handle;
+ /* Handle to CVP in the Q6 */
+ u16 cvp_passive_handle;
+ u16 cvp_full_handle;
+
+ struct mutex lock;
+
+ struct mvs_driver_info mvs_info;
+
+ uint16_t sidetone_gain;
+
+ struct voice_dev_route_state voc_route_state;
+};
+
+int voc_set_voc_path_full(uint32_t set);
+
+void voc_register_mvs_cb(ul_cb_fn ul_cb,
+ dl_cb_fn dl_cb,
+ void *private_data);
+
+void voc_config_vocoder(uint32_t media_type,
+ uint32_t rate,
+ uint32_t network_type);
+
+enum {
+ DEV_RX = 0,
+ DEV_TX,
+};
+
+enum {
+ RX_PATH = 0,
+ TX_PATH,
+};
+
+/* called by alsa driver */
+int voc_start_voice_call(void);
+int voc_end_voice_call(void);
+void voc_set_rxtx_port(uint32_t dev_port_id, uint32_t dev_type);
+int voc_set_rx_vol_index(uint32_t dir, uint32_t voc_idx);
+int voc_set_tx_mute(uint32_t dir, uint32_t mute);
+int voc_disable_cvp(void);
+int voc_enable_cvp(void);
+void voc_set_route_flag(uint8_t path_dir, uint8_t set);
+uint8_t voc_get_route_flag(uint8_t path_dir);
+
+#endif
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
index 9465588..f16f02b 100644
--- a/sound/soc/samsung/dma.c
+++ b/sound/soc/samsung/dma.c
@@ -10,8 +10,7 @@
*
* 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.
+ * Free Software Foundation; only version 2 of the License.
*/
#include <linux/slab.h>
@@ -497,5 +496,5 @@
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:samsung-audio");
diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
index f8f6816..c004645 100644
--- a/sound/soc/sh/siu_pcm.c
+++ b/sound/soc/sh/siu_pcm.c
@@ -6,8 +6,7 @@
*
* 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.
+ * the Free Software Foundation; only version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 1dc6de6..87569ed 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2,12 +2,12 @@
* soc-dapm.c -- ALSA SoC Dynamic Audio Power Management
*
* Copyright 2005 Wolfson Microelectronics PLC.
+ *
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* 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.
+ * Free Software Foundation; only version 2 of the License.
*
* Features:
* o Changes power status of internal codec blocks depending on the
@@ -1545,7 +1545,6 @@
dapm->dev_power = 1;
break;
case SND_SOC_DAPM_STREAM_STOP:
-#warning need re-work
if (dapm->codec)
dapm->dev_power = !!dapm->codec->active;
else
@@ -3073,4 +3072,4 @@
/* Module information */
MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/soc-dsp.c b/sound/soc/soc-dsp.c
index 9323b57..77eda32 100644
--- a/sound/soc/soc-dsp.c
+++ b/sound/soc/soc-dsp.c
@@ -5,10 +5,9 @@
*
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
- * 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.
+ * 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.
*
*/
@@ -1511,4 +1510,4 @@
/* Module information */
MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("ALSA SoC DSP Core");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");