M7350/kernel/arch/arm/mach-msm/qdsp6/audiov2/voice.c
2024-09-09 08:52:07 +00:00

189 lines
4.0 KiB
C

/* 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/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/completion.h>
#include <linux/wait.h>
#include <mach/msm_qdsp6_audiov2.h>
#include "../dal.h"
#include "dal_voice.h"
#include <mach/debug_mm.h>
struct voice_struct {
struct dal_client *cvd;
struct apr_command_pkt apr_pkt;
struct completion compl;
};
static struct voice_struct voice;
static int cvd_send_response(void)
{
struct apr_command_pkt *pkt;
uint16_t src_addr;
uint16_t src_token;
uint16_t dst_token;
uint16_t dst_addr;
pkt = &voice.apr_pkt;
src_addr = pkt->dst_addr;
dst_addr = pkt->src_addr;
src_token = pkt->dst_token;
dst_token = pkt->src_token;
pkt->header &= ~APR_PKTV1_TYPE_MASK;
pkt->header |= APR_SET_FIELD(APR_PKTV1_TYPE, APR_PKTV1_TYPE_EVENT_V);
pkt->src_addr = src_addr;
pkt->dst_addr = dst_addr;
pkt->src_token = src_token;
pkt->dst_token = dst_token;
pkt->opcode = APR_IBASIC_RSP_RESULT;
dal_call(voice.cvd, VOICE_OP_CONTROL, 5, pkt,
sizeof(struct apr_command_pkt),
pkt, sizeof(u32));
return 0;
}
static int cvd_process_voice_setup(void)
{
q6voice_setup();
cvd_send_response();
return 0;
}
static int cvd_process_voice_teardown(void)
{
q6voice_teardown();
cvd_send_response();
return 0;
}
static int cvd_process_set_network(void)
{
cvd_send_response();
return 0;
}
static int voice_thread(void *data)
{
while (!kthread_should_stop()) {
wait_for_completion(&voice.compl);
init_completion(&voice.compl);
switch (voice.apr_pkt.opcode) {
case APR_OP_CMD_CREATE:
cvd_send_response();
break;
case VOICE_OP_CMD_BRINGUP:
cvd_process_voice_setup();
break;
case APR_OP_CMD_DESTROY:
cvd_send_response();
break;
case VOICE_OP_CMD_TEARDOWN:
cvd_process_voice_teardown();
break;
case VOICE_OP_CMD_SET_NETWORK:
cvd_process_set_network();
break;
default:
pr_err("[%s:%s] Undefined event\n", __MM_FILE__,
__func__);
}
}
return 0;
}
static void remote_cb_function(void *data, int len, void *cookie)
{
struct apr_command_pkt *apr = data + 2*sizeof(uint32_t);
memcpy(&voice.apr_pkt, apr, sizeof(struct apr_command_pkt));
if (len <= 0) {
pr_err("[%s:%s] unexpected event with length %d\n",
__MM_FILE__, __func__, len);
return;
}
pr_debug("[%s:%s] APR = %x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", __MM_FILE__,
__func__,
apr->header,
apr->reserved1,
apr->src_addr,
apr->dst_addr,
apr->ret_addr,
apr->src_token,
apr->dst_token,
apr->ret_token,
apr->context,
apr->opcode);
complete(&voice.compl);
}
static int __init voice_init(void)
{
int res = 0;
struct task_struct *task;
u32 tmp[2];
tmp[0] = sizeof(u32);
tmp[1] = 0;
voice.cvd = dal_attach(VOICE_DAL_DEVICE, VOICE_DAL_PORT, 0,
remote_cb_function, 0);
if (!voice.cvd) {
pr_err("[%s:%s] audio_init: cannot attach to cvd\n",
__MM_FILE__, __func__);
res = -ENODEV;
goto done;
}
if (check_version(voice.cvd, VOICE_DAL_VERSION) != 0) {
pr_err("[%s:%s] Incompatible cvd version\n",
__MM_FILE__, __func__);
res = -ENODEV;
goto done;
}
dal_call(voice.cvd, VOICE_OP_INIT, 5, tmp, sizeof(tmp),
tmp, sizeof(u32));
init_completion(&voice.compl);
task = kthread_run(voice_thread, &voice, "voice_thread");
if (IS_ERR(task)) {
pr_err("[%s:%s] Cannot start the voice thread\n", __MM_FILE__,
__func__);
res = PTR_ERR(task);
task = NULL;
} else
goto done;
done:
return res;
}
late_initcall(voice_init);