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);