kernel_samsung_a53x/sound/soc/samsung/abox/abox_synchronized_ipc.c
2024-06-15 16:02:09 -03:00

321 lines
8.6 KiB
C
Executable file

/* sound/soc/samsung/abox/abox_synchronized_ipc.c
*
* ALSA SoC Audio Layer - Samsung Abox synchronized IPC driver
*
* Copyright (c) 2020 Samsung Electronics Co. Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define DEBUG
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
/*#include <sound/abox_synchronized_ipc.h>*/
#include <sound/tfa_ext.h>
#include "abox.h"
#include "abox_dma.h"
#define TFADSP_CMD_WRITE 0x1001
#define TFADSP_CMD_READ 0x1002
#define TFADSP_CMD_BLACKBOX 0x1003
#define TFADSP_RES_WRITE 0x2001
#define TFADSP_RES_READ 0x2002
#define TFADSP_RES_BLACKBOX 0x2003
#define TFADSP_SUCCESS 0
#define TFADSP_WRITE_INVALID_STATE 1
#define TFADSP_INVALID_PARAM 2
#define TFADSP_FAILED 3
#define TFADSP_READ_INVALID_STATE 4
#define REALTIME_GEAR_ID 0x7007
#define VENDORID_TFADSP_ID 0x778
//#define SMEM_READ 0x818FC000
//#define SMEM_WRITE 0x818FE000
#define SMEM_READ 0xA0780000
#define SMEM_WRITE 0xA0782000
#define TIMEOUT_MS 100
#define DEBUG_SYNCHRONIZED_IPC
#ifdef DEBUG_SYNCHRONIZED_IPC
#define ipc_dbg(format, args...) \
pr_info("[SYNC_IPC] %s: " format "\n", __func__, ## args)
#else
#define ipc_dbg(format, args...)
#endif /* DEBUG_SYNCHRONIZED_IPC */
#define ipc_err(format, args...) \
pr_err("[SYNC_IPC] %s: " format "\n", __func__, ## args)
static DECLARE_WAIT_QUEUE_HEAD(wq_read);
static DECLARE_WAIT_QUEUE_HEAD(wq_write);
struct abox_dma_data *data;
char *tfadrv_read_buf = NULL;
char *tfadrv_write_buf = NULL;
char *smem_write_buf = NULL;
char *smem_read_buf = NULL;
int abox_ipc_read_error = 0;
int abox_ipc_write_error = 0;
bool abox_ipc_read_avail = false;
bool abox_ipc_write_avail = false;
int tfadsp_read(void *tfa, int length, unsigned char *buf)
{
ABOX_IPC_MSG msg;
int ret = 0;
struct IPC_ERAP_MSG *erap_msg = &msg.msg.erap;
ipc_dbg("length = %d", length);
abox_request_cpu_gear_ext(data->dev_abox, REALTIME_GEAR_ID, 4 ,"TFA9874");
tfadrv_read_buf = (char *)buf;
msg.ipcid = IPC_ERAP;
erap_msg->msgtype = REALTIME_EXTRA;
erap_msg->param.raw.params[0] = VENDORID_TFADSP_ID;
erap_msg->param.raw.params[1] = TFADSP_CMD_READ;
erap_msg->param.raw.params[2] = length;
abox_ipc_read_avail = false;
abox_ipc_read_error = TFADSP_SUCCESS;
ret = abox_request_ipc(data->dev_abox, IPC_ERAP,
&msg, sizeof(msg), 0, 0);
if (ret < 0) {
ipc_err("abox_start_ipc_transaction is failed, error=%d", ret);
/*return -1;*/
}
ret = wait_event_timeout(wq_read,
abox_ipc_read_avail, msecs_to_jiffies(TIMEOUT_MS));
abox_request_cpu_gear_ext(data->dev_abox, REALTIME_GEAR_ID, 12, "TFA9874");
if (!ret) {
ipc_err("wait_event timeout");
return -1;
}
if (abox_ipc_read_error) {
ipc_err("error = %d", abox_ipc_read_error);
return -1;
}
return 0;
}
EXPORT_SYMBOL_GPL(tfadsp_read);
int tfadsp_write(void *tfa, int length, const char *buf)
{
ABOX_IPC_MSG msg;
int ret = 0;
struct IPC_ERAP_MSG *erap_msg = &msg.msg.erap;
ipc_dbg("length = %d", length);
abox_request_cpu_gear_ext(data->dev_abox, REALTIME_GEAR_ID, 4, "TFA9874");
tfadrv_write_buf = (char *)buf;
smem_write_buf = (char *)abox_iova_to_virt(data->dev_abox, SMEM_WRITE);
msg.ipcid = IPC_ERAP;
erap_msg->msgtype = REALTIME_EXTRA;
erap_msg->param.raw.params[0] = VENDORID_TFADSP_ID;
erap_msg->param.raw.params[1] = TFADSP_CMD_WRITE;
erap_msg->param.raw.params[2] = length;
memcpy(smem_write_buf, tfadrv_write_buf, length);
abox_ipc_write_avail = false;
abox_ipc_write_error = TFADSP_SUCCESS;
ret = abox_request_ipc(data->dev_abox, IPC_ERAP,
&msg, sizeof(msg), 0, 0);
if (ret < 0) {
ipc_err("abox_start_ipc_transaction is failed, error=%d", ret);
/*return -1;*/
}
ret = wait_event_timeout(wq_write,
abox_ipc_write_avail, msecs_to_jiffies(TIMEOUT_MS));
abox_request_cpu_gear_ext(data->dev_abox, REALTIME_GEAR_ID, 12, "TFA9874");
if (!ret) {
ipc_err("wait_event timeout");
return -1;
}
if (abox_ipc_write_error) {
ipc_err("abox_ipc_write_error = %d", abox_ipc_write_error);
return -1;
}
return 0;
}
EXPORT_SYMBOL_GPL(tfadsp_write);
static irqreturn_t abox_synchronized_ipc_handler(int irq,
void *dev_id, ABOX_IPC_MSG *msg)
{
struct IPC_ERAP_MSG *erap_msg = &msg->msg.erap;
unsigned int res_id = erap_msg->param.raw.params[0];
unsigned int size = erap_msg->param.raw.params[1];
irqreturn_t ret = IRQ_HANDLED;
switch (irq) {
case IPC_ERAP:
switch (erap_msg->msgtype) {
case REALTIME_EXTRA:
switch(res_id) {
case TFADSP_RES_READ:
smem_read_buf =
(char *)abox_iova_to_virt(data->dev_abox, SMEM_READ);
memcpy(tfadrv_read_buf, smem_read_buf, size);
abox_ipc_read_avail = true;
abox_ipc_read_error = TFADSP_SUCCESS;
ipc_dbg("TFADSP_CMD_READ DONE size= %d", size);
if (waitqueue_active(&wq_read))
wake_up(&wq_read);
break;
case TFADSP_RES_BLACKBOX:
smem_read_buf =
(char *)abox_iova_to_virt(data->dev_abox, SMEM_READ);
memcpy(tfadrv_read_buf, smem_read_buf, size);
abox_ipc_read_avail = true;
abox_ipc_read_error = TFADSP_SUCCESS;
ipc_dbg("TFADSP_CMD_BLACKBOX DONE size= %d", size);
if (waitqueue_active(&wq_read))
wake_up(&wq_read);
break;
case TFADSP_READ_INVALID_STATE:
abox_ipc_read_avail = true;
abox_ipc_read_error = TFADSP_READ_INVALID_STATE;
ipc_err("tfadsp_read() TFADSP_READ_INVALID_STATE");
if (waitqueue_active(&wq_read))
wake_up(&wq_read);
break;
case TFADSP_RES_WRITE:
abox_ipc_write_avail = true;
abox_ipc_write_error = TFADSP_SUCCESS;
ipc_dbg("TFADSP_CMD_WRITE done");
if (waitqueue_active(&wq_write))
wake_up(&wq_write);
break;
case TFADSP_WRITE_INVALID_STATE:
abox_ipc_write_avail = true;
abox_ipc_write_error = TFADSP_WRITE_INVALID_STATE;
ipc_err("tfadsp_write() TFADSP_WRITE_INVALID_STATE");
if (waitqueue_active(&wq_write))
wake_up(&wq_write);
break;
case TFADSP_INVALID_PARAM:
abox_ipc_write_avail = true;
abox_ipc_write_error = TFADSP_INVALID_PARAM;
ipc_err("tfadsp_write() TFADSP_INVALID_PARAM");
if (waitqueue_active(&wq_write))
wake_up(&wq_write);
break;
case TFADSP_FAILED:
abox_ipc_write_avail = true;
abox_ipc_write_error = TFADSP_FAILED;
ipc_err("tfadsp_write() TFADSP_FAILED");
if (waitqueue_active(&wq_write))
wake_up(&wq_write);
break;
default:
ipc_err("unknown response type, RES_ID = 0x%x, size=%d", res_id, size);
ret = IRQ_NONE;
break;
}
break;
default:
ipc_err("unknown message type, msgtype = %d",
erap_msg->msgtype);
ret = IRQ_NONE;
}
break;
default:
ipc_err("unknown command, irq = %d", irq);
ret = IRQ_NONE;
break;
}
return ret;
}
static int samsung_abox_synchronized_ipc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *np_abox;
struct platform_device *pdev_abox;
ipc_dbg(" ");
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data) {
dev_err(dev, "[SYNC_IPC] Failed to allocate memory\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, data);
np_abox = of_parse_phandle(np, "abox", 0);
if (!np_abox) {
dev_err(dev, "[SYNC_IPC] Failed to get abox device node\n");
return -EPROBE_DEFER;
}
pdev_abox = of_find_device_by_node(np_abox);
data->dev_abox = &pdev_abox->dev;
if (!data->dev_abox) {
dev_err(dev, "[SYNC_IPC] Failed to get abox platform device\n");
return -EPROBE_DEFER;
}
data->abox_data = platform_get_drvdata(pdev_abox);
abox_register_ipc_handler(data->dev_abox, IPC_ERAP,
abox_synchronized_ipc_handler, pdev);
tfa_ext_register((dsp_send_message_t)tfadsp_write,
(dsp_read_message_t)tfadsp_read, NULL);
return 0;
}
static int samsung_abox_synchronized_ipc_remove(struct platform_device *pdev)
{
ipc_dbg(" ");
return 0;
}
static const struct of_device_id samsung_abox_synchronized_ipc_match[] = {
{
.compatible = "samsung,abox-synchronized-ipc",
},
{},
};
MODULE_DEVICE_TABLE(of, samsung_abox_synchronized_ipc_match);
static struct platform_driver samsung_abox_synchronized_ipc_driver = {
.probe = samsung_abox_synchronized_ipc_probe,
.remove = samsung_abox_synchronized_ipc_remove,
.driver = {
.name = "samsung-abox-synchronized-ipc",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(samsung_abox_synchronized_ipc_match),
},
};
module_platform_driver(samsung_abox_synchronized_ipc_driver);
/* Module information */
MODULE_AUTHOR("SeokYoung Jang, <quartz.jang@samsung.com>");
MODULE_DESCRIPTION("Samsung ASoC A-Box Synchronized IPC Driver");
MODULE_ALIAS("platform:samsung-abox-synchronized-ipc");
MODULE_LICENSE("GPL");