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/arch/arm/mach-msm/qdsp6/dtmf.c b/arch/arm/mach-msm/qdsp6/dtmf.c
new file mode 100644
index 0000000..cf27488
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp6/dtmf.c
@@ -0,0 +1,126 @@
+/* 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/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/msm_qdsp6_audio.h>
+#include <mach/debug_mm.h>
+
+struct dtmf {
+ struct mutex lock;
+ struct audio_client *ac;
+ struct msm_dtmf_config cfg;
+};
+
+static long dtmf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct dtmf *dtmf = file->private_data;
+ int rc = 0;
+
+ mutex_lock(&dtmf->lock);
+ switch (cmd) {
+
+ case AUDIO_START: {
+ pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
+ if (dtmf->ac) {
+ pr_err("[%s:%s] active session already existing\n",
+ __MM_FILE__, __func__);
+ rc = -EBUSY;
+ } else {
+ dtmf->ac = q6audio_open_dtmf(48000, 2, 0);
+ if (!dtmf->ac)
+ rc = -ENOMEM;
+ }
+ break;
+ }
+ case AUDIO_PLAY_DTMF: {
+ rc = copy_from_user((void *)&dtmf->cfg, (void *)arg,
+ sizeof(struct msm_dtmf_config));
+
+ pr_debug("[%s:%s] PLAY_DTMF: high = %d, low = %d\n",
+ __MM_FILE__, __func__, dtmf->cfg.dtmf_hi,
+ dtmf->cfg.dtmf_low);
+ rc = q6audio_play_dtmf(dtmf->ac, dtmf->cfg.dtmf_hi,
+ dtmf->cfg.dtmf_low, dtmf->cfg.duration,
+ dtmf->cfg.rx_gain);
+ if (rc) {
+ pr_err("[%s:%s] DTMF_START failed\n", __MM_FILE__,
+ __func__);
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ mutex_unlock(&dtmf->lock);
+
+ pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc) ;
+ return rc;
+}
+
+static int dtmf_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ struct dtmf *dtmf;
+ pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
+ dtmf = kzalloc(sizeof(struct dtmf), GFP_KERNEL);
+
+ if (!dtmf)
+ return -ENOMEM;
+
+ mutex_init(&dtmf->lock);
+
+ file->private_data = dtmf;
+ return rc;
+}
+
+static int dtmf_release(struct inode *inode, struct file *file)
+{
+ struct dtmf *dtmf = file->private_data;
+ if (dtmf->ac)
+ q6audio_close(dtmf->ac);
+ kfree(dtmf);
+ pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
+ return 0;
+}
+
+static const struct file_operations dtmf_fops = {
+ .owner = THIS_MODULE,
+ .open = dtmf_open,
+ .release = dtmf_release,
+ .unlocked_ioctl = dtmf_ioctl,
+};
+
+struct miscdevice dtmf_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_dtmf",
+ .fops = &dtmf_fops,
+};
+
+static int __init dtmf_init(void)
+{
+ return misc_register(&dtmf_misc);
+}
+
+device_initcall(dtmf_init);