162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci// Copyright 2017-2020 NXP
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/rpmsg.h>
662306a36Sopenharmony_ci#include "imx-pcm-rpmsg.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * struct imx_audio_rpmsg: private data
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * @rpmsg_pdev: pointer of platform device
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_cistruct imx_audio_rpmsg {
1462306a36Sopenharmony_ci	struct platform_device *rpmsg_pdev;
1562306a36Sopenharmony_ci};
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
1862306a36Sopenharmony_ci			      void *priv, u32 src)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct imx_audio_rpmsg *rpmsg = dev_get_drvdata(&rpdev->dev);
2162306a36Sopenharmony_ci	struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data;
2262306a36Sopenharmony_ci	struct rpmsg_info *info;
2362306a36Sopenharmony_ci	struct rpmsg_msg *msg;
2462306a36Sopenharmony_ci	unsigned long flags;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	if (!rpmsg->rpmsg_pdev)
2762306a36Sopenharmony_ci		return 0;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	info = platform_get_drvdata(rpmsg->rpmsg_pdev);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	dev_dbg(&rpdev->dev, "get from%d: cmd:%d. %d\n",
3262306a36Sopenharmony_ci		src, r_msg->header.cmd, r_msg->param.resp);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	switch (r_msg->header.type) {
3562306a36Sopenharmony_ci	case MSG_TYPE_C:
3662306a36Sopenharmony_ci		/* TYPE C is notification from M core */
3762306a36Sopenharmony_ci		switch (r_msg->header.cmd) {
3862306a36Sopenharmony_ci		case TX_PERIOD_DONE:
3962306a36Sopenharmony_ci			spin_lock_irqsave(&info->lock[TX], flags);
4062306a36Sopenharmony_ci			msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
4162306a36Sopenharmony_ci			msg->r_msg.param.buffer_tail =
4262306a36Sopenharmony_ci						r_msg->param.buffer_tail;
4362306a36Sopenharmony_ci			msg->r_msg.param.buffer_tail %= info->num_period[TX];
4462306a36Sopenharmony_ci			spin_unlock_irqrestore(&info->lock[TX], flags);
4562306a36Sopenharmony_ci			info->callback[TX](info->callback_param[TX]);
4662306a36Sopenharmony_ci			break;
4762306a36Sopenharmony_ci		case RX_PERIOD_DONE:
4862306a36Sopenharmony_ci			spin_lock_irqsave(&info->lock[RX], flags);
4962306a36Sopenharmony_ci			msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
5062306a36Sopenharmony_ci			msg->r_msg.param.buffer_tail =
5162306a36Sopenharmony_ci						r_msg->param.buffer_tail;
5262306a36Sopenharmony_ci			msg->r_msg.param.buffer_tail %= info->num_period[1];
5362306a36Sopenharmony_ci			spin_unlock_irqrestore(&info->lock[RX], flags);
5462306a36Sopenharmony_ci			info->callback[RX](info->callback_param[RX]);
5562306a36Sopenharmony_ci			break;
5662306a36Sopenharmony_ci		default:
5762306a36Sopenharmony_ci			dev_warn(&rpdev->dev, "unknown msg command\n");
5862306a36Sopenharmony_ci			break;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci		break;
6162306a36Sopenharmony_ci	case MSG_TYPE_B:
6262306a36Sopenharmony_ci		/* TYPE B is response msg */
6362306a36Sopenharmony_ci		memcpy(&info->r_msg, r_msg, sizeof(struct rpmsg_r_msg));
6462306a36Sopenharmony_ci		complete(&info->cmd_complete);
6562306a36Sopenharmony_ci		break;
6662306a36Sopenharmony_ci	default:
6762306a36Sopenharmony_ci		dev_warn(&rpdev->dev, "unknown msg type\n");
6862306a36Sopenharmony_ci		break;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return 0;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int imx_audio_rpmsg_probe(struct rpmsg_device *rpdev)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct imx_audio_rpmsg *data;
7762306a36Sopenharmony_ci	int ret = 0;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
8062306a36Sopenharmony_ci		 rpdev->src, rpdev->dst);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	data = devm_kzalloc(&rpdev->dev, sizeof(*data), GFP_KERNEL);
8362306a36Sopenharmony_ci	if (!data)
8462306a36Sopenharmony_ci		return -ENOMEM;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	dev_set_drvdata(&rpdev->dev, data);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Register platform driver for rpmsg routine */
8962306a36Sopenharmony_ci	data->rpmsg_pdev = platform_device_register_data(&rpdev->dev,
9062306a36Sopenharmony_ci							 IMX_PCM_DRV_NAME,
9162306a36Sopenharmony_ci							 PLATFORM_DEVID_AUTO,
9262306a36Sopenharmony_ci							 NULL, 0);
9362306a36Sopenharmony_ci	if (IS_ERR(data->rpmsg_pdev)) {
9462306a36Sopenharmony_ci		dev_err(&rpdev->dev, "failed to register rpmsg platform.\n");
9562306a36Sopenharmony_ci		ret = PTR_ERR(data->rpmsg_pdev);
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return ret;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void imx_audio_rpmsg_remove(struct rpmsg_device *rpdev)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct imx_audio_rpmsg *data = dev_get_drvdata(&rpdev->dev);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (data->rpmsg_pdev)
10662306a36Sopenharmony_ci		platform_device_unregister(data->rpmsg_pdev);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	dev_info(&rpdev->dev, "audio rpmsg driver is removed\n");
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic struct rpmsg_device_id imx_audio_rpmsg_id_table[] = {
11262306a36Sopenharmony_ci	{ .name	= "rpmsg-audio-channel" },
11362306a36Sopenharmony_ci	{ .name = "rpmsg-micfil-channel" },
11462306a36Sopenharmony_ci	{ },
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct rpmsg_driver imx_audio_rpmsg_driver = {
11862306a36Sopenharmony_ci	.drv.name	= "imx_audio_rpmsg",
11962306a36Sopenharmony_ci	.id_table	= imx_audio_rpmsg_id_table,
12062306a36Sopenharmony_ci	.probe		= imx_audio_rpmsg_probe,
12162306a36Sopenharmony_ci	.callback	= imx_audio_rpmsg_cb,
12262306a36Sopenharmony_ci	.remove		= imx_audio_rpmsg_remove,
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cimodule_rpmsg_driver(imx_audio_rpmsg_driver);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale SoC Audio RPMSG interface");
12862306a36Sopenharmony_ciMODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
12962306a36Sopenharmony_ciMODULE_ALIAS("platform:imx_audio_rpmsg");
13062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
131