/* Copyright (c) 2009-2011, The Linux Foundation. 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 #include #include #include #include #include #include #include #include #include #include #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");