1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-07-14 08:44:45 +00:00
Files
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

749 lines
17 KiB
C

// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2016 MediaTek Inc.
#include <linux/module.h> /* needed by all modules */
#include <linux/init.h> /* needed by module macros */
#include <linux/fs.h> /* needed by file_operations* */
#include <linux/miscdevice.h> /* needed by miscdevice* */
#include <linux/sysfs.h>
#include <linux/device.h> /* needed by device_* */
#include <linux/vmalloc.h> /* needed by vmalloc */
#include <linux/uaccess.h> /* needed by copy_to_user */
#include <linux/poll.h> /* needed by poll */
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/suspend.h>
#include <linux/timer.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_fdt.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <audio_ipi_queue.h>
#if IS_ENABLED(CONFIG_MTK_SCP_AUDIO)
#include <scp_helper.h>
#include <scp_audio_ipi.h>
#include <scp_feature_define.h>
#endif
#if IS_ENABLED(CONFIG_MTK_AUDIODSP_SUPPORT)
#include <adsp_helper.h>
#endif
#include <audio_log.h>
#include <audio_assert.h>
#include <audio_controller_msg_id.h>
#include <audio_messenger_ipi.h>
#include <audio_task_manager.h>
#include <audio_ipi_dma.h>
#include <audio_ipi_platform.h>
#include <adsp_ipi_queue.h>
/*
* =============================================================================
* log
* =============================================================================
*/
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) "[IPI][DRV] %s(), " fmt "\n", __func__
/*
* =============================================================================
* MACRO
* =============================================================================
*/
#define AUDIO_IPI_DEVICE_NAME "audio_ipi"
#define AUDIO_IPI_IOC_MAGIC 'i'
#define AUDIO_IPI_IOCTL_SEND_MSG_ONLY _IOW(AUDIO_IPI_IOC_MAGIC, 0, unsigned int)
#define AUDIO_IPI_IOCTL_SEND_PAYLOAD _IOW(AUDIO_IPI_IOC_MAGIC, 1, unsigned int)
#define AUDIO_IPI_IOCTL_SEND_DRAM _IOW(AUDIO_IPI_IOC_MAGIC, 2, unsigned int)
#define AUDIO_IPI_IOCTL_INIT_DSP _IOW(AUDIO_IPI_IOC_MAGIC, 20, unsigned int)
#define AUDIO_IPI_IOCTL_REG_DMA _IOW(AUDIO_IPI_IOC_MAGIC, 21, unsigned int)
/*
* =============================================================================
* struct
* =============================================================================
*/
struct audio_ipi_reg_dma_t {
uint32_t magic_header;
uint8_t task;
uint8_t reg_flag; /* 1: register, 0: unregister */
uint16_t __reserved;
uint32_t a2d_size;
uint32_t d2a_size;
uint32_t magic_footer;
};
struct audio_ipi_reg_feature_t {
uint16_t reg_flag;
uint16_t feature_id;
};
struct audio_task_info_t {
uint32_t dsp_id; /* dsp_id_t */
uint8_t is_dsp_support; /* dsp_id supported or not */
uint8_t is_adsp; /* adsp(HiFi) or not */
uint8_t is_scp; /* scp(CM4) or not */
uint8_t task_ctrl; /* task controller scene # */
};
/*
* =============================================================================
* global var
* =============================================================================
*/
static struct audio_task_info_t g_audio_task_info[TASK_SCENE_SIZE];
static DEFINE_MUTEX(init_dsp_lock);
static DEFINE_MUTEX(reg_dma_lock);
/*
* =============================================================================
* implementation
* =============================================================================
*/
inline uint32_t msg_len_of_type(const uint8_t data_type)
{
uint32_t msg_len = 0;
switch (data_type) {
case AUDIO_IPI_MSG_ONLY:
msg_len = IPI_MSG_HEADER_SIZE;
break;
case AUDIO_IPI_PAYLOAD:
msg_len = MAX_IPI_MSG_BUF_SIZE;
break;
case AUDIO_IPI_DMA:
msg_len = IPI_MSG_HEADER_SIZE + IPI_MSG_DMA_INFO_SIZE;
break;
default:
pr_info("%d not support!!", data_type);
msg_len = IPI_MSG_HEADER_SIZE;
}
return msg_len;
}
static int parsing_ipi_msg_from_user_space(
void __user *user_data_ptr,
uint8_t data_type)
{
struct ipi_msg_t ipi_msg;
struct ipi_msg_dma_info_t *dma_info = NULL;
struct aud_data_t *wb_dram = NULL;
uint32_t msg_len = 0;
uint32_t hal_data_size = 0;
void *copy_hal_data = NULL;
uint32_t hal_wb_buf_size = 0;
void __user *hal_wb_buf_addr = NULL;
phys_addr_t dram_buf = 0;
void *dram_buf_virt = NULL;
int retval = 0;
/* get message size to read */
msg_len = msg_len_of_type(data_type);
if (msg_len > sizeof(struct ipi_msg_t)) {
pr_notice("msg_len %u > %zu!!",
msg_len, sizeof(struct ipi_msg_t));
retval = -1;
goto parsing_exit;
}
memset(&ipi_msg, 0, sizeof(struct ipi_msg_t));
retval = copy_from_user(&ipi_msg, user_data_ptr, msg_len);
if (retval != 0) {
pr_notice("msg copy_from_user retval %d", retval);
goto parsing_exit;
}
if (ipi_msg.data_type != data_type) { /* double check */
pr_notice("data_type %d != %d", ipi_msg.data_type, data_type);
retval = -1;
goto parsing_exit;
}
if (ipi_msg.source_layer != AUDIO_IPI_LAYER_FROM_HAL) {
pr_notice("source_layer %d != %d", ipi_msg.source_layer, AUDIO_IPI_LAYER_FROM_HAL);
retval = -1;
goto parsing_exit;
}
msg_len = get_message_buf_size(&ipi_msg);
retval = check_msg_format(&ipi_msg, msg_len);
if (retval != 0)
goto parsing_exit;
ipi_dbg("task %d msg 0x%x", ipi_msg.task_scene, ipi_msg.msg_id);
/* get dma buf if need */
dma_info = &ipi_msg.dma_info;
wb_dram = &dma_info->wb_dram;
if (data_type == AUDIO_IPI_DMA) {
/* check DMA & Write-Back size */
hal_data_size = dma_info->hal_buf.data_size;
if (hal_data_size > MAX_DSP_DMA_WRITE_SIZE) {
DUMP_IPI_MSG("hal_data_size error!!", &ipi_msg);
retval = -1;
goto parsing_exit;
}
hal_wb_buf_size = dma_info->hal_buf.memory_size;
if (hal_wb_buf_size > MAX_DSP_DMA_WRITE_SIZE) {
DUMP_IPI_MSG("hal_wb_buf_size error!!", &ipi_msg);
retval = -1;
goto parsing_exit;
}
/* get hal data & write hal data to DRAM */
copy_hal_data = vmalloc(hal_data_size);
if (copy_hal_data == NULL) {
retval = -ENOMEM;
goto parsing_exit;
}
retval = copy_from_user(
copy_hal_data,
(void __user *)dma_info->hal_buf.addr,
hal_data_size);
if (retval != 0) {
pr_notice("dma copy_from_user retval %d", retval);
goto parsing_exit;
}
dma_info->data_size = hal_data_size;
retval = audio_ipi_dma_write_region(
ipi_msg.task_scene,
copy_hal_data,
hal_data_size,
&dma_info->rw_idx);
if (retval != 0) {
pr_notice("dma write region error!!");
goto parsing_exit;
}
/* write back result to hal later, like get parameter */
hal_wb_buf_addr = (void __user *)dma_info->hal_buf.addr;
ipi_dbg(
"write region copy_hal_data(%p), hal_data_size %d, hal_wb_buf_size %d"
, copy_hal_data, hal_data_size, hal_wb_buf_size);
if (hal_wb_buf_size != 0 && hal_wb_buf_addr != NULL) {
/* alloc a dma for wb */
audio_ipi_dma_alloc(ipi_msg.task_scene,
&dram_buf,
&dram_buf_virt,
hal_wb_buf_size);
wb_dram->memory_size = hal_wb_buf_size;
wb_dram->data_size = 0;
wb_dram->addr_val = dram_buf;
ipi_dbg(
"hal_wb_buf_addr(%p), wb dram_buf(0x%x), hal_wb_buf_size(%d)"
, hal_wb_buf_addr, (uint32_t)dram_buf, hal_wb_buf_size);
/* force need ack to get dsp info */
if (ipi_msg.ack_type != AUDIO_IPI_MSG_NEED_ACK) {
pr_notice("task %d msg 0x%x need ack!!",
ipi_msg.task_scene, ipi_msg.msg_id);
ipi_msg.ack_type = AUDIO_IPI_MSG_NEED_ACK;
}
}
#ifdef DEBUG_IPI
DUMP_IPI_MSG("dma", &ipi_msg);
#endif
}
/* sent message */
retval = audio_send_ipi_filled_msg(&ipi_msg);
if (retval != 0) {
pr_notice("audio_send_ipi_filled_msg error!!");
goto parsing_exit;
}
/* write back data to hal */
if (data_type == AUDIO_IPI_DMA &&
hal_wb_buf_size != 0 &&
hal_wb_buf_addr != NULL &&
wb_dram != NULL &&
wb_dram->addr_val != 0 &&
ipi_msg.dsp_ret == 1) {
if (wb_dram->data_size > hal_wb_buf_size) {
pr_notice("wb_dram->data_size %u > hal_wb_buf_size %u!!",
wb_dram->data_size,
hal_wb_buf_size);
ipi_msg.dsp_ret = 0;
} else if (wb_dram->data_size == 0) {
pr_notice("ipi wb data sz = 0!! check adsp write");
ipi_msg.dsp_ret = 0;
} else {
retval = copy_to_user(
hal_wb_buf_addr,
dram_buf_virt,
wb_dram->data_size);
if (retval) {
pr_info("copy_to_user dma err, id = 0x%x",
ipi_msg.msg_id);
ipi_msg.dsp_ret = 0;
}
}
}
/* write back ipi msg to hal */
if (data_type == AUDIO_IPI_DMA) /* clear sensitive addr info */
memset(&ipi_msg.dma_info, 0, IPI_MSG_DMA_INFO_SIZE);
retval = copy_to_user(user_data_ptr,
&ipi_msg,
sizeof(struct ipi_msg_t));
if (retval) {
pr_info("copy_to_user err, id = 0x%x", ipi_msg.msg_id);
retval = -EFAULT;
}
parsing_exit:
if (copy_hal_data != NULL) {
vfree(copy_hal_data);
copy_hal_data = NULL;
}
if (dram_buf != 0) {
ipi_dbg(
"task %d msg 0x%x, free wb buffer 0x%x, hal_wb_buf_size 0x%x"
, ipi_msg.task_scene, ipi_msg.msg_id,
(uint32_t)dram_buf, hal_wb_buf_size);
audio_ipi_dma_free(ipi_msg.task_scene,
dram_buf, hal_wb_buf_size);
}
ipi_dbg("task %d msg 0x%x, retval %d",
ipi_msg.task_scene, ipi_msg.msg_id, retval);
return retval;
}
#if IS_ENABLED(CONFIG_MTK_AUDIODSP_SUPPORT)
/* ADSP reboot */
static int audio_ctrl_event_receive(
struct notifier_block *this,
unsigned long event,
void *ptr)
{
struct ipi_queue_handler_t *handler = NULL;
uint8_t scene = 0;
uint32_t dsp_id = 0;
switch (event) {
case ADSP_EVENT_STOP:
for (scene = 0; scene < TASK_SCENE_SIZE; scene++) {
if (is_audio_use_adsp(audio_get_dsp_id(scene))) {
handler = get_ipi_queue_handler(scene);
if (handler != NULL)
flush_ipi_queue_handler(handler);
}
}
break;
case ADSP_EVENT_READY:
for (dsp_id = 0; dsp_id < NUM_OPENDSP_TYPE; dsp_id++) {
if (is_audio_use_adsp(dsp_id))
audio_ipi_dma_init_dsp(dsp_id);
}
break;
default:
pr_info("event %lu err", event);
}
return 0;
}
static struct notifier_block audio_ctrl_notifier = {
.notifier_call = audio_ctrl_event_receive,
.priority = AUDIO_CONTROLLER_FEATURE_PRI,
};
/* HAL reboot */
static int audio_ipi_init_dsp_hifi3(const uint32_t dsp_id)
{
static bool init_flag[NUM_OPENDSP_TYPE];
uint8_t task = TASK_SCENE_INVALID;
struct ipi_msg_t ipi_msg;
int ret = 0;
if (dsp_id >= NUM_OPENDSP_TYPE) {
pr_info("dsp_id(%u) invalid!!!", dsp_id);
return -ENODEV;
}
if (!is_audio_use_adsp(dsp_id)) {
pr_info("dsp_id(%u) not for adsp!!!", dsp_id);
return -ENODEV;
}
task = get_audio_controller_task(dsp_id);
if (task == TASK_SCENE_INVALID) {
pr_info("task(%d) invalid!!!", task);
return -ENODEV;
}
/* check phone boot */
if (init_flag[dsp_id] == false) {
init_flag[dsp_id] = true;
pr_info("phone init");
audio_ipi_dma_init_dsp(dsp_id);
return 0;
}
/* wake up adsp */
adsp_register_feature(AUDIO_CONTROLLER_FEATURE_ID);
/* not first init => HAL reboot */
pr_info("audio hal reinit");
/* TODO: do something before ADSP process hal reboot here */
/* ADSP process hal reboot here */
ret = audio_send_ipi_msg(
&ipi_msg,
task,
AUDIO_IPI_LAYER_TO_DSP,
AUDIO_IPI_MSG_ONLY,
AUDIO_IPI_MSG_NEED_ACK,
AUD_CTL_MSG_A2D_HAL_REBOOT,
0, /* 0: audio HAL */
0,
NULL);
/* release DMA */
audio_ipi_dma_free_region_all_task(dsp_id);
/* allow adsp to sleep */
adsp_deregister_feature(AUDIO_CONTROLLER_FEATURE_ID);
/* TODO: clear feature register count here? */
reset_hal_feature_table();
return ret;
}
#endif
#if IS_ENABLED(CONFIG_MTK_SCP_AUDIO)
/* CM4 reboot */
static int audio_ctrl_event_receive_scp(
struct notifier_block *this,
unsigned long event,
void *ptr)
{
struct ipi_queue_handler_t *handler = NULL;
uint8_t scene = 0;
switch (event) {
case SCP_EVENT_STOP:
for (scene = 0; scene < TASK_SCENE_SIZE; scene++) {
if (audio_get_dsp_id(scene) ==
AUDIO_OPENDSP_USE_RV_A) {
handler = get_ipi_queue_handler(scene);
if (handler != NULL)
flush_ipi_queue_handler(handler);
}
}
break;
case SCP_EVENT_READY:
audio_ipi_dma_init_dsp(AUDIO_OPENDSP_USE_RV_A);
break;
default:
pr_info("event %lu err", event);
}
return 0;
}
static struct notifier_block audio_ctrl_notifier_scp = {
.notifier_call = audio_ctrl_event_receive_scp,
};
static int audio_ipi_init_dsp_rv(void)
{
int ret = 0;
struct ipi_msg_t ipi_msg;
static bool init_flag;
/* check phone boot */
if (init_flag == false) {
init_flag = true;
pr_info("phone init");
audio_ipi_dma_init_dsp(AUDIO_OPENDSP_USE_RV_A);
return 0;
}
/* not first init => HAL reboot */
pr_info("audio hal reinit");
/* TODO: do something before CM4 process hal reboot here */
/* CM4 process hal reboot here */
ret = audio_send_ipi_msg(
&ipi_msg,
TASK_SCENE_AUDIO_CONTROLLER_RV,
AUDIO_IPI_LAYER_TO_DSP,
AUDIO_IPI_MSG_ONLY,
AUDIO_IPI_MSG_NEED_ACK,
AUD_CTL_MSG_A2D_HAL_REBOOT,
0, /* 0: audio HAL */
0,
NULL);
/* release DMA */
audio_ipi_dma_free_region_all_task(AUDIO_OPENDSP_USE_RV_A);
return ret;
}
#endif /* end of CONFIG_MTK_SCP_AUDIO */
static long audio_ipi_ioctl(
struct file *file, unsigned int cmd, unsigned long arg)
{
#if IS_ENABLED(CONFIG_MTK_AUDIODSP_SUPPORT)
uint32_t dsp_id = 0;
#endif
struct audio_ipi_reg_dma_t dma_reg;
int retval = 0;
uint32_t check_sum = 0;
AUD_LOG_V("cmd = %u, arg = %lu", cmd, arg);
switch (cmd) {
case AUDIO_IPI_IOCTL_SEND_MSG_ONLY: {
retval = parsing_ipi_msg_from_user_space(
(void __user *)arg, AUDIO_IPI_MSG_ONLY);
break;
}
case AUDIO_IPI_IOCTL_SEND_PAYLOAD: {
retval = parsing_ipi_msg_from_user_space(
(void __user *)arg, AUDIO_IPI_PAYLOAD);
break;
}
case AUDIO_IPI_IOCTL_SEND_DRAM: {
retval = parsing_ipi_msg_from_user_space(
(void __user *)arg, AUDIO_IPI_DMA);
break;
}
case AUDIO_IPI_IOCTL_INIT_DSP: {
pr_debug("AUDIO_IPI_IOCTL_INIT_DSP");
mutex_lock(&init_dsp_lock);
#if IS_ENABLED(CONFIG_MTK_AUDIODSP_SUPPORT)
for (dsp_id = 0; dsp_id < NUM_OPENDSP_TYPE; dsp_id++) {
if (is_audio_use_adsp(dsp_id))
audio_ipi_init_dsp_hifi3(dsp_id);
}
#endif
#if IS_ENABLED(CONFIG_MTK_SCP_AUDIO)
if (is_audio_scp_support())
audio_ipi_init_dsp_rv();
#endif
/* copy g_audio_task_info to HAL */
if (((void __user *)arg) == NULL)
retval = -EINVAL;
else {
retval = copy_to_user((void __user *)arg,
g_audio_task_info,
sizeof(g_audio_task_info));
}
if (retval) {
pr_info("task info copy_to_user err");
retval = -EFAULT;
}
mutex_unlock(&init_dsp_lock);
break;
}
case AUDIO_IPI_IOCTL_REG_DMA: {
if (((void __user *)arg) == NULL) {
retval = -1;
break;
}
retval = copy_from_user(
&dma_reg,
(void __user *)arg,
sizeof(struct audio_ipi_reg_dma_t));
if (retval != 0) {
pr_notice("dma reg copy_from_user retval %d", retval);
break;
}
check_sum = dma_reg.magic_footer + dma_reg.magic_header;
if (check_sum != 0xFFFFFFFF) {
pr_notice("dma reg check fail! header(0x%x) footer(0x%x)",
dma_reg.magic_header,
dma_reg.magic_footer);
retval = -1;
break;
}
mutex_lock(&reg_dma_lock);
if (dma_reg.reg_flag)
retval = audio_ipi_dma_alloc_region(dma_reg.task,
dma_reg.a2d_size,
dma_reg.d2a_size);
else
retval = audio_ipi_dma_free_region(dma_reg.task);
mutex_unlock(&reg_dma_lock);
break;
}
default:
retval = -ENOIOCTLCMD;
break;
}
return retval;
}
#ifdef CONFIG_COMPAT
static long audio_ipi_compat_ioctl(
struct file *file, unsigned int cmd, unsigned long arg)
{
if (!file->f_op || !file->f_op->unlocked_ioctl) {
pr_notice("op null");
return -ENOTTY;
}
return file->f_op->unlocked_ioctl(file, cmd, arg);
}
#endif
static ssize_t audio_ipi_read(
struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
return audio_ipi_dma_msg_read(buf, count);
}
static const struct file_operations audio_ipi_ops = {
.owner = THIS_MODULE,
.read = audio_ipi_read,
.unlocked_ioctl = audio_ipi_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = audio_ipi_compat_ioctl,
#endif
};
static struct miscdevice audio_ipi_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = AUDIO_IPI_DEVICE_NAME,
.fops = &audio_ipi_ops
};
static int __init audio_ipi_init(void)
{
uint32_t dsp_id = 0;
struct audio_task_info_t *task_info = NULL;
uint8_t task_id = 0;
int ret = 0;
ipi_queue_init();
audio_task_manager_init();
audio_messenger_ipi_init();
init_audio_ipi_dma();
#if IS_ENABLED(CONFIG_MTK_AUDIODSP_SUPPORT)
adsp_register_notify(&audio_ctrl_notifier);
#endif
#if IS_ENABLED(CONFIG_MTK_SCP_AUDIO)
if (is_audio_scp_support())
scp_A_register_notify(&audio_ctrl_notifier_scp);
#endif
for (task_id = 0; task_id < TASK_SCENE_SIZE; task_id++) {
task_info = &g_audio_task_info[task_id];
dsp_id = audio_get_dsp_id(task_id);
task_info->dsp_id = dsp_id;
task_info->is_dsp_support = is_audio_dsp_support(dsp_id);
task_info->is_adsp = is_audio_use_adsp(dsp_id);
task_info->is_scp = is_audio_use_scp(dsp_id);
task_info->task_ctrl = get_audio_controller_task(dsp_id);
}
ret = misc_register(&audio_ipi_device);
if (unlikely(ret != 0)) {
pr_notice("misc register failed");
return ret;
}
return ret;
}
static void __exit audio_ipi_exit(void)
{
audio_task_manager_deinit();
deinit_audio_ipi_dma();
misc_deregister(&audio_ipi_device);
}
module_init(audio_ipi_init);
module_exit(audio_ipi_exit);
MODULE_AUTHOR("Harvey Huang <harvey.huang@mediatek.com>");
MODULE_DESCRIPTION("MTK AUDIO IPI Device Driver");
MODULE_LICENSE("GPL");