| /* Copyright (c) 2010, 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 <linux/cdev.h> |
| #include <linux/fs.h> |
| #include <linux/list.h> |
| #include <linux/platform_device.h> |
| #include <linux/sched.h> |
| #include <linux/uaccess.h> |
| #include <linux/msm_adsp.h> |
| #include <linux/module.h> |
| |
| #include <mach/qdsp5/qdsp5rmtcmdi.h> |
| #include <mach/qdsp5/qdsp5rmtmsg.h> |
| #include <mach/debug_mm.h> |
| #include "adsp.h" |
| |
| #define MAX_CLIENTS 5 |
| #define MAX_AUDIO_CLIENTS 5 |
| #define MAX_RM_CLIENTS MAX_AUDIO_CLIENTS |
| |
| static char *rm_errs[] = { |
| "", |
| "PCM Blocks not Sufficient", |
| "TASK is already occupied", |
| "Concurrency not supported", |
| "MIPS not sufficient", |
| "DDP invalid/no licence" |
| }; |
| static struct client { |
| wait_queue_head_t wait; |
| unsigned int wait_state; |
| struct aud_codec_config_ack cfg_msg; |
| } rmclient[MAX_RM_CLIENTS]; |
| |
| static struct rm { |
| struct msm_adsp_module *mod; |
| int cnt; |
| int state; |
| |
| struct aud_codec_config_ack cfg_msg; |
| struct mutex lock; |
| } rmtask; |
| |
| static void rm_dsp_event(void *data, unsigned id, size_t len, |
| void (*getevent) (void *ptr, size_t len)); |
| static struct msm_adsp_ops rm_ops = { |
| .event = rm_dsp_event, |
| }; |
| |
| int32_t get_adsp_resource(unsigned short client_id, |
| void *cmd_buf, size_t cmd_size) |
| { |
| int rc = 0; |
| int client_idx; |
| |
| client_idx = ((client_id >> 8) * MAX_CLIENTS) + (client_id & 0xFF); |
| if (client_idx >= MAX_RM_CLIENTS) |
| return -EINVAL; |
| |
| mutex_lock(&rmtask.lock); |
| if (rmtask.state != ADSP_STATE_ENABLED) { |
| rc = msm_adsp_get("RMTASK", &rmtask.mod, &rm_ops, NULL); |
| if (rc) { |
| MM_ERR("Failed to get module RMTASK\n"); |
| mutex_unlock(&rmtask.lock); |
| return rc; |
| } |
| rc = msm_adsp_enable(rmtask.mod); |
| if (rc) { |
| MM_ERR("RMTASK enable Failed\n"); |
| msm_adsp_put(rmtask.mod); |
| mutex_unlock(&rmtask.lock); |
| return rc; |
| } |
| rmtask.state = ADSP_STATE_ENABLED; |
| } |
| rmclient[client_idx].wait_state = -1; |
| mutex_unlock(&rmtask.lock); |
| msm_adsp_write(rmtask.mod, QDSP_apuRmtQueue, cmd_buf, cmd_size); |
| rc = wait_event_interruptible_timeout(rmclient[client_idx].wait, |
| rmclient[client_idx].wait_state != -1, 5 * HZ); |
| mutex_lock(&rmtask.lock); |
| if (unlikely(rc < 0)) { |
| if (rc == -ERESTARTSYS) |
| MM_ERR("wait_event_interruptible " |
| "returned -ERESTARTSYS\n"); |
| else |
| MM_ERR("wait_event_interruptible " |
| "returned error\n"); |
| if (!rmtask.cnt) |
| goto disable_rm; |
| goto unlock; |
| } else if (rc == 0) { |
| MM_ERR("RMTASK Msg not received\n"); |
| rc = -ETIMEDOUT; |
| if (!rmtask.cnt) |
| goto disable_rm; |
| goto unlock; |
| } |
| if (!(rmclient[client_idx].cfg_msg.enable)) { |
| MM_ERR("Reason for failure: %s\n", |
| rm_errs[rmclient[client_idx].cfg_msg.reason]); |
| rc = -EBUSY; |
| if (!rmtask.cnt) |
| goto disable_rm; |
| goto unlock; |
| } |
| rmtask.cnt++; |
| mutex_unlock(&rmtask.lock); |
| return 0; |
| |
| disable_rm: |
| msm_adsp_disable(rmtask.mod); |
| msm_adsp_put(rmtask.mod); |
| rmtask.state = ADSP_STATE_DISABLED; |
| unlock: |
| mutex_unlock(&rmtask.lock); |
| return rc; |
| } |
| EXPORT_SYMBOL(get_adsp_resource); |
| |
| int32_t put_adsp_resource(unsigned short client_id, void *cmd_buf, |
| size_t cmd_size) |
| { |
| mutex_lock(&rmtask.lock); |
| if (rmtask.state != ADSP_STATE_ENABLED) { |
| mutex_unlock(&rmtask.lock); |
| return 0; |
| } |
| |
| msm_adsp_write(rmtask.mod, QDSP_apuRmtQueue, cmd_buf, cmd_size); |
| rmtask.cnt--; |
| if (!rmtask.cnt) { |
| msm_adsp_disable(rmtask.mod); |
| msm_adsp_put(rmtask.mod); |
| rmtask.state = ADSP_STATE_DISABLED; |
| } |
| mutex_unlock(&rmtask.lock); |
| return 0; |
| } |
| EXPORT_SYMBOL(put_adsp_resource); |
| |
| static void rm_dsp_event(void *data, unsigned id, size_t len, |
| void (*getevent) (void *ptr, size_t len)) |
| { |
| unsigned short client_id; |
| int client_idx; |
| |
| MM_DBG("Msg ID = %d\n", id); |
| |
| switch (id) { |
| case RMT_CODEC_CONFIG_ACK: { |
| getevent(&rmtask.cfg_msg, sizeof(rmtask.cfg_msg)); |
| client_id = ((rmtask.cfg_msg.client_id << 8) | |
| rmtask.cfg_msg.task_id); |
| client_idx = ((client_id >> 8) * MAX_CLIENTS) + |
| (client_id & 0xFF); |
| memcpy(&rmclient[client_idx].cfg_msg, &rmtask.cfg_msg, |
| sizeof(rmtask.cfg_msg)); |
| rmclient[client_idx].wait_state = 1; |
| wake_up(&rmclient[client_idx].wait); |
| break; |
| } |
| case RMT_DSP_OUT_OF_MIPS: { |
| struct rmt_dsp_out_of_mips msg; |
| getevent(&msg, sizeof(msg)); |
| MM_ERR("RMT_DSP_OUT_OF_MIPS: Not enough resorces in ADSP \ |
| to handle all sessions :%hx\n", msg.dec_info); |
| break; |
| } |
| default: |
| MM_DBG("Unknown Msg Id\n"); |
| break; |
| } |
| } |
| |
| void rmtask_init(void) |
| { |
| int i; |
| |
| for (i = 0; i < MAX_RM_CLIENTS; i++) |
| init_waitqueue_head(&rmclient[i].wait); |
| mutex_init(&rmtask.lock); |
| } |