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/qdsp5v2/afe.c b/arch/arm/mach-msm/qdsp5v2/afe.c
new file mode 100644
index 0000000..20c9898
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/afe.c
@@ -0,0 +1,534 @@
+/* Copyright (c) 2009-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/wait.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+#include <mach/qdsp5v2/qdsp5afecmdi.h>
+#include <mach/qdsp5v2/qdsp5afemsg.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/msm_adsp.h>
+#include <mach/debug_mm.h>
+
+#define AFE_MAX_TIMEOUT 500 /* 500 ms */
+#define AFE_MAX_CLNT 6 /* 6 HW path defined so far */
+#define GETDEVICEID(x) ((x) - 1)
+
+struct msm_afe_state {
+	struct msm_adsp_module *mod;
+	struct msm_adsp_ops    adsp_ops;
+	struct mutex           lock;
+	u8                     in_use;
+	u8                     codec_config[AFE_MAX_CLNT];
+	wait_queue_head_t      wait;
+	u8			aux_conf_flag;
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_afelb;
+#endif
+
+
+static struct msm_afe_state the_afe_state;
+
+#define afe_send_queue(afe, cmd, len) \
+  msm_adsp_write(afe->mod, QDSP_apuAfeQueue, \
+	cmd, len)
+
+static void afe_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct msm_afe_state *afe = data;
+
+	MM_DBG("msg_id %d \n", id);
+
+	switch (id) {
+	case AFE_APU_MSG_CODEC_CONFIG_ACK: {
+		struct afe_msg_codec_config_ack afe_ack;
+		getevent(&afe_ack, AFE_APU_MSG_CODEC_CONFIG_ACK_LEN);
+		MM_DBG("%s: device_id: %d device activity: %d\n", __func__,
+		afe_ack.device_id, afe_ack.device_activity);
+		if (afe_ack.device_activity == AFE_MSG_CODEC_CONFIG_DISABLED)
+			afe->codec_config[GETDEVICEID(afe_ack.device_id)] = 0;
+		else
+			afe->codec_config[GETDEVICEID(afe_ack.device_id)] =
+			afe_ack.device_activity;
+
+		wake_up(&afe->wait);
+		break;
+	}
+	case AFE_APU_MSG_VOC_TIMING_SUCCESS:
+		MM_INFO("Received VOC_TIMING_SUCCESS message from AFETASK\n");
+		break;
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable/disable(audpptask)");
+		break;
+	default:
+		MM_ERR("unexpected message from afe \n");
+	}
+
+	return;
+}
+
+static void afe_dsp_codec_config(struct msm_afe_state *afe,
+	u8 path_id, u8 enable, struct msm_afe_config *config)
+{
+	struct afe_cmd_codec_config cmd;
+
+	MM_DBG("%s() %p\n", __func__, config);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_CODEC_CONFIG_CMD;
+	cmd.device_id = path_id;
+	cmd.activity = enable;
+	if (config) {
+		MM_DBG("%s: sample_rate %x ch mode %x vol %x\n",
+			__func__, config->sample_rate,
+			config->channel_mode, config->volume);
+		cmd.sample_rate = config->sample_rate;
+		cmd.channel_mode = config->channel_mode;
+		cmd.volume = config->volume;
+	}
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+}
+/* Function is called after afe module been enabled */
+void afe_loopback(int enable)
+{
+	struct afe_cmd_loopback cmd;
+	struct msm_afe_state *afe;
+
+	afe = &the_afe_state;
+	MM_DBG("enable %d\n", enable);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_LOOPBACK;
+	if (enable)
+		cmd.enable_flag = AFE_LOOPBACK_ENABLE_COMMAND;
+
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(afe_loopback);
+
+void afe_ext_loopback(int enable, int rx_copp_id, int tx_copp_id)
+{
+	struct afe_cmd_ext_loopback cmd;
+	struct msm_afe_state *afe;
+
+	afe = &the_afe_state;
+	MM_DBG("enable %d\n", enable);
+	if ((rx_copp_id == 0) && (tx_copp_id == 0)) {
+		afe_loopback(enable);
+	} else {
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.cmd_id = AFE_CMD_EXT_LOOPBACK;
+		cmd.source_id = tx_copp_id;
+		cmd.dst_id = rx_copp_id;
+		if (enable)
+			cmd.enable_flag = AFE_LOOPBACK_ENABLE_COMMAND;
+
+		afe_send_queue(afe, &cmd, sizeof(cmd));
+	}
+}
+EXPORT_SYMBOL(afe_ext_loopback);
+
+void afe_device_volume_ctrl(u16 device_id, u16 device_volume)
+{
+	struct afe_cmd_device_volume_ctrl cmd;
+	struct msm_afe_state *afe;
+
+	afe = &the_afe_state;
+	MM_DBG("device 0x%4x volume 0x%4x\n", device_id, device_volume);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_DEVICE_VOLUME_CTRL;
+	cmd.device_id = device_id;
+	cmd.device_volume = device_volume;
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(afe_device_volume_ctrl);
+
+int afe_enable(u8 path_id, struct msm_afe_config *config)
+{
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc;
+
+	MM_DBG("%s: path %d\n", __func__, path_id);
+	if ((GETDEVICEID(path_id) < 0) || (GETDEVICEID(path_id) > 5)) {
+		MM_ERR("Invalid path_id: %d\n", path_id);
+		return -EINVAL;
+	}
+	mutex_lock(&afe->lock);
+	if (!afe->in_use && !afe->aux_conf_flag) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_ERR("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	/* Issue codec config command */
+	afe_dsp_codec_config(afe, path_id, 1, config);
+	rc = wait_event_timeout(afe->wait,
+		afe->codec_config[GETDEVICEID(path_id)],
+		msecs_to_jiffies(AFE_MAX_TIMEOUT));
+	if (!rc) {
+		MM_ERR("AFE failed to respond within %d ms\n", AFE_MAX_TIMEOUT);
+		rc = -ENODEV;
+		if (!afe->in_use) {
+			if (!afe->aux_conf_flag ||
+			(afe->aux_conf_flag &&
+			(path_id == AFE_HW_PATH_AUXPCM_RX ||
+			path_id == AFE_HW_PATH_AUXPCM_TX))) {
+				/* clean up if there is no client */
+				msm_adsp_disable(afe->mod);
+				msm_adsp_put(afe->mod);
+				afe->aux_conf_flag = 0;
+				afe->mod = NULL;
+			}
+		}
+
+	} else {
+		rc = 0;
+		afe->in_use++;
+	}
+
+	mutex_unlock(&afe->lock);
+	return rc;
+
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_enable);
+
+int afe_config_fm_codec(int fm_enable, uint16_t source)
+{
+	struct afe_cmd_fm_codec_config cmd;
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc = 0;
+	int i = 0;
+	unsigned short *ptrmem = (unsigned short *)&cmd;
+
+	MM_INFO(" configure fm codec\n");
+	mutex_lock(&afe->lock);
+	if (!afe->in_use) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_ERR("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_FM_RX_ROUTING_CMD;
+	cmd.enable = fm_enable;
+	cmd.device_id = source;
+
+	for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+		MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+
+	mutex_unlock(&afe->lock);
+	return rc;
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_config_fm_codec);
+
+int afe_config_fm_volume(uint16_t volume)
+{
+	struct afe_cmd_fm_volume_config cmd;
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc = 0;
+
+	MM_INFO(" configure fm volume\n");
+	mutex_lock(&afe->lock);
+	if (!afe->in_use) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_ERR("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_FM_PLAYBACK_VOLUME_CMD;
+	cmd.volume = volume;
+
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+
+	mutex_unlock(&afe->lock);
+	return rc;
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_config_fm_volume);
+
+int afe_config_fm_calibration_gain(uint16_t device_id,
+			uint16_t calibration_gain)
+{
+	struct afe_cmd_fm_calibgain_config cmd;
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc = 0;
+
+	MM_INFO("Configure for rx device = 0x%4x, gain = 0x%4x\n", device_id,
+			calibration_gain);
+	mutex_lock(&afe->lock);
+	if (!afe->in_use) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_ERR("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_FM_CALIBRATION_GAIN_CMD;
+	cmd.device_id = device_id;
+	cmd.calibration_gain = calibration_gain;
+
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+
+	mutex_unlock(&afe->lock);
+	return rc;
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_config_fm_calibration_gain);
+
+int afe_config_aux_codec(int pcm_ctl_value, int aux_codec_intf_value,
+				int data_format_pad)
+{
+	struct afe_cmd_aux_codec_config cmd;
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc = 0;
+
+	MM_DBG(" configure aux codec \n");
+	mutex_lock(&afe->lock);
+	if (!afe->in_use && !afe->aux_conf_flag) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_ERR("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	afe->aux_conf_flag = 1;
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_AUX_CODEC_CONFIG_CMD;
+	cmd.dma_path_ctl = 0;
+	cmd.pcm_ctl = pcm_ctl_value;
+	cmd.eight_khz_int_mode = 0;
+	cmd.aux_codec_intf_ctl = aux_codec_intf_value;
+	cmd.data_format_padding_info = data_format_pad;
+
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+
+	mutex_unlock(&afe->lock);
+	return rc;
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_config_aux_codec);
+
+int afe_config_rmc_block(struct acdb_rmc_block *acdb_rmc)
+{
+	struct afe_cmd_cfg_rmc cmd;
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc = 0;
+	int i = 0;
+	unsigned short *ptrmem = (unsigned short *)&cmd;
+
+	MM_DBG(" configure rmc block\n");
+	mutex_lock(&afe->lock);
+	if (!afe->in_use && !afe->mod) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_DBG("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_CFG_RMC_PARAMS;
+
+	cmd.rmc_mode = acdb_rmc->rmc_enable;
+	cmd.rmc_ipw_length_ms =	acdb_rmc->rmc_ipw_length_ms;
+	cmd.rmc_peak_length_ms = acdb_rmc->rmc_peak_length_ms;
+	cmd.rmc_init_pulse_length_ms = acdb_rmc->rmc_init_pulse_length_ms;
+	cmd.rmc_total_int_length_ms = acdb_rmc->rmc_total_int_length_ms;
+	cmd.rmc_rampupdn_length_ms = acdb_rmc->rmc_rampupdn_length_ms;
+	cmd.rmc_delay_length_ms = acdb_rmc->rmc_delay_length_ms;
+	cmd.rmc_detect_start_threshdb = acdb_rmc->rmc_detect_start_threshdb;
+	cmd.rmc_init_pulse_threshdb = acdb_rmc->rmc_init_pulse_threshdb;
+
+	for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+		MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+
+	mutex_unlock(&afe->lock);
+	return rc;
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_config_rmc_block);
+
+int afe_disable(u8 path_id)
+{
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc;
+
+	mutex_lock(&afe->lock);
+
+	BUG_ON(!afe->in_use);
+	MM_DBG("%s() path_id:%d codec state:%d\n", __func__, path_id,
+	afe->codec_config[GETDEVICEID(path_id)]);
+	afe_dsp_codec_config(afe, path_id, 0, NULL);
+	rc = wait_event_timeout(afe->wait,
+		!afe->codec_config[GETDEVICEID(path_id)],
+		msecs_to_jiffies(AFE_MAX_TIMEOUT));
+	if (!rc) {
+		MM_ERR("AFE failed to respond within %d ms\n", AFE_MAX_TIMEOUT);
+		rc = -1;
+	} else
+		rc = 0;
+	afe->in_use--;
+	MM_DBG("%s() in_use:%d \n", __func__, afe->in_use);
+	if (!afe->in_use) {
+		msm_adsp_disable(afe->mod);
+		msm_adsp_put(afe->mod);
+		afe->aux_conf_flag = 0;
+		afe->mod = NULL;
+	}
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_disable);
+
+
+#ifdef CONFIG_DEBUG_FS
+static int afe_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	MM_INFO("debug intf %s\n", (char *) file->private_data);
+	return 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 cmd;
+
+	if (get_user(cmd, ubuf))
+		return -EFAULT;
+
+	MM_INFO("%s %c\n", lb_str, cmd);
+
+	if (!strcmp(lb_str, "afe_loopback")) {
+		switch (cmd) {
+		case '1':
+			afe_loopback(1);
+			break;
+		case '0':
+			afe_loopback(0);
+			break;
+		}
+	}
+
+	return cnt;
+}
+
+static const struct file_operations afe_debug_fops = {
+	.open = afe_debug_open,
+	.write = afe_debug_write
+};
+#endif
+
+static int __init afe_init(void)
+{
+	struct msm_afe_state *afe = &the_afe_state;
+
+	MM_INFO("AFE driver init\n");
+
+	memset(afe, 0, sizeof(struct msm_afe_state));
+	afe->adsp_ops.event = afe_dsp_event;
+	mutex_init(&afe->lock);
+	init_waitqueue_head(&afe->wait);
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_afelb = debugfs_create_file("afe_loopback",
+	S_IFREG | S_IWUGO, NULL, (void *) "afe_loopback",
+	&afe_debug_fops);
+#endif
+
+	return 0;
+}
+
+static void __exit afe_exit(void)
+{
+	MM_INFO("AFE driver exit\n");
+#ifdef CONFIG_DEBUG_FS
+	if (debugfs_afelb)
+		debugfs_remove(debugfs_afelb);
+#endif
+	if (the_afe_state.mod)
+		msm_adsp_put(the_afe_state.mod);
+	return;
+}
+
+module_init(afe_init);
+module_exit(afe_exit);
+
+MODULE_DESCRIPTION("MSM AFE driver");
+MODULE_LICENSE("GPL v2");