lineage_kernel_xcoverpro/sound/soc/samsung/abox/abox_synchronized_ipc.c

313 lines
8.7 KiB
C
Raw Permalink Normal View History

2023-06-18 22:53:49 +00:00
/* sound/soc/samsung/abox/abox_synchronized_ipc.c
*
* ALSA SoC Audio Layer - Samsung Abox synchronized IPC driver
*
* Copyright (c) 2017 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.
*/
#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"
#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 SMEM_READ 0x80584000
#define SMEM_WRITE 0x80582000
/*#define SMEM_READ 0x80840000*/
/*#define SMEM_WRITE 0x80842000*/
#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);
static struct abox_platform_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(int devidx, int size, void *buf)
{
ABOX_IPC_MSG msg;
int ret = 0;
struct IPC_ERAP_MSG *erap_msg = &msg.msg.erap;
struct abox_data *aboxdata = abox_get_abox_data();
ipc_dbg("size = %d", size);
abox_request_cpu_gear(&data->pdev_abox->dev, aboxdata,
REALTIME_GEAR_ID, 4);
tfadrv_read_buf = (char *)buf;
msg.ipcid = IPC_ERAP;
erap_msg->msgtype = REALTIME_EXTRA;
erap_msg->param.raw.params[0] = TFADSP_CMD_READ;
erap_msg->param.raw.params[1] = size;
abox_ipc_read_avail = false;
abox_ipc_read_error = TFADSP_SUCCESS;
ret = abox_start_ipc_transaction(&data->pdev_abox->dev, IPC_ERAP,
&msg, sizeof(msg), 1, 1);
if (ret < 0) {
ipc_err("abox_start_ipc_transaction is failed, error=%d", ret);
/*return -1;*/
}
ret = wait_event_interruptible_timeout(wq_read,
abox_ipc_read_avail, msecs_to_jiffies(TIMEOUT_MS));
abox_request_cpu_gear(&data->pdev_abox->dev, aboxdata,
REALTIME_GEAR_ID, 12);
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(int devidx, int size, void *buf)
{
ABOX_IPC_MSG msg;
int ret = 0;
struct IPC_ERAP_MSG *erap_msg = &msg.msg.erap;
struct abox_data *aboxdata = abox_get_abox_data();
ipc_dbg("size = %d", size);
abox_request_cpu_gear(&data->pdev_abox->dev, aboxdata,
REALTIME_GEAR_ID, 4);
tfadrv_write_buf = (char *)buf;
smem_write_buf = (char *)abox_addr_to_kernel_addr(aboxdata, SMEM_WRITE);
msg.ipcid = IPC_ERAP;
erap_msg->msgtype = REALTIME_EXTRA;
erap_msg->param.raw.params[0] = TFADSP_CMD_WRITE;
erap_msg->param.raw.params[1] = size;
memcpy(smem_write_buf, tfadrv_write_buf, size);
abox_ipc_write_avail = false;
abox_ipc_write_error = TFADSP_SUCCESS;
ret = abox_start_ipc_transaction(&data->pdev_abox->dev, IPC_ERAP,
&msg, sizeof(msg), 1, 1);
if (ret < 0) {
ipc_err("abox_start_ipc_transaction is failed, error=%d", ret);
/*return -1;*/
}
ret = wait_event_interruptible_timeout(wq_write,
abox_ipc_write_avail, msecs_to_jiffies(TIMEOUT_MS));
abox_request_cpu_gear(&data->pdev_abox->dev, aboxdata,
REALTIME_GEAR_ID, 12);
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;
struct abox_data *aboxdata = abox_get_abox_data();
unsigned int res_id = erap_msg->param.raw.params[0];
unsigned int size = erap_msg->param.raw.params[1];
switch (irq) {
case IPC_ERAP:
switch (erap_msg->msgtype) {
case REALTIME_EXTRA:
switch(res_id) {
case TFADSP_RES_READ:
smem_read_buf =
(char *)abox_addr_to_kernel_addr(aboxdata, 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_interruptible(&wq_read);
break;
case TFADSP_RES_BLACKBOX:
smem_read_buf =
(char *)abox_addr_to_kernel_addr(aboxdata, 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_interruptible(&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_interruptible(&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_interruptible(&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_interruptible(&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_interruptible(&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_interruptible(&wq_write);
break;
default:
ipc_err("unknown response type, RES_ID = %d", res_id);
break;
}
break;
default:
ipc_err("unknown message type, msgtype = %d",
erap_msg->msgtype);
}
break;
default:
ipc_err("unknown command, irq = %d", irq);
break;
}
return IRQ_HANDLED;
}
static struct snd_soc_platform_driver abox_synchronized_ipc = {
};
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;
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;
}
data->pdev_abox = of_find_device_by_node(np_abox);
if (!data->pdev_abox) {
dev_err(dev, "[SYNC_IPC] Failed to get abox platform device\n");
return -EPROBE_DEFER;
}
data->abox_data = platform_get_drvdata(data->pdev_abox);
abox_register_irq_handler(&data->pdev_abox->dev, IPC_ERAP,
abox_synchronized_ipc_handler, pdev);
tfa_ext_register((dsp_send_message_t)tfadsp_write,
(dsp_read_message_t)tfadsp_read, NULL);
return snd_soc_register_platform(&pdev->dev, &abox_synchronized_ipc);
}
static int samsung_abox_synchronized_ipc_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&pdev->dev);
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");