1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // Copyright 2017-2020 NXP |
3 | |
4 | #include <linux/module.h> |
5 | #include <linux/rpmsg.h> |
6 | #include "imx-pcm-rpmsg.h" |
7 | |
8 | /* |
9 | * struct imx_audio_rpmsg: private data |
10 | * |
11 | * @rpmsg_pdev: pointer of platform device |
12 | */ |
13 | struct imx_audio_rpmsg { |
14 | struct platform_device *rpmsg_pdev; |
15 | }; |
16 | |
17 | static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, |
18 | void *priv, u32 src) |
19 | { |
20 | struct imx_audio_rpmsg *rpmsg = dev_get_drvdata(dev: &rpdev->dev); |
21 | struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data; |
22 | struct rpmsg_info *info; |
23 | struct rpmsg_msg *msg; |
24 | unsigned long flags; |
25 | |
26 | if (!rpmsg->rpmsg_pdev) |
27 | return 0; |
28 | |
29 | info = platform_get_drvdata(pdev: rpmsg->rpmsg_pdev); |
30 | |
31 | dev_dbg(&rpdev->dev, "get from%d: cmd:%d. %d\n" , |
32 | src, r_msg->header.cmd, r_msg->param.resp); |
33 | |
34 | switch (r_msg->header.type) { |
35 | case MSG_TYPE_C: |
36 | /* TYPE C is notification from M core */ |
37 | switch (r_msg->header.cmd) { |
38 | case TX_PERIOD_DONE: |
39 | spin_lock_irqsave(&info->lock[TX], flags); |
40 | msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; |
41 | msg->r_msg.param.buffer_tail = |
42 | r_msg->param.buffer_tail; |
43 | msg->r_msg.param.buffer_tail %= info->num_period[TX]; |
44 | spin_unlock_irqrestore(lock: &info->lock[TX], flags); |
45 | info->callback[TX](info->callback_param[TX]); |
46 | break; |
47 | case RX_PERIOD_DONE: |
48 | spin_lock_irqsave(&info->lock[RX], flags); |
49 | msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; |
50 | msg->r_msg.param.buffer_tail = |
51 | r_msg->param.buffer_tail; |
52 | msg->r_msg.param.buffer_tail %= info->num_period[1]; |
53 | spin_unlock_irqrestore(lock: &info->lock[RX], flags); |
54 | info->callback[RX](info->callback_param[RX]); |
55 | break; |
56 | default: |
57 | dev_warn(&rpdev->dev, "unknown msg command\n" ); |
58 | break; |
59 | } |
60 | break; |
61 | case MSG_TYPE_B: |
62 | /* TYPE B is response msg */ |
63 | memcpy(&info->r_msg, r_msg, sizeof(struct rpmsg_r_msg)); |
64 | complete(&info->cmd_complete); |
65 | break; |
66 | default: |
67 | dev_warn(&rpdev->dev, "unknown msg type\n" ); |
68 | break; |
69 | } |
70 | |
71 | return 0; |
72 | } |
73 | |
74 | static int imx_audio_rpmsg_probe(struct rpmsg_device *rpdev) |
75 | { |
76 | struct imx_audio_rpmsg *data; |
77 | int ret = 0; |
78 | |
79 | dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n" , |
80 | rpdev->src, rpdev->dst); |
81 | |
82 | data = devm_kzalloc(dev: &rpdev->dev, size: sizeof(*data), GFP_KERNEL); |
83 | if (!data) |
84 | return -ENOMEM; |
85 | |
86 | dev_set_drvdata(dev: &rpdev->dev, data); |
87 | |
88 | /* Register platform driver for rpmsg routine */ |
89 | data->rpmsg_pdev = platform_device_register_data(parent: &rpdev->dev, |
90 | IMX_PCM_DRV_NAME, |
91 | PLATFORM_DEVID_AUTO, |
92 | NULL, size: 0); |
93 | if (IS_ERR(ptr: data->rpmsg_pdev)) { |
94 | dev_err(&rpdev->dev, "failed to register rpmsg platform.\n" ); |
95 | ret = PTR_ERR(ptr: data->rpmsg_pdev); |
96 | } |
97 | |
98 | return ret; |
99 | } |
100 | |
101 | static void imx_audio_rpmsg_remove(struct rpmsg_device *rpdev) |
102 | { |
103 | struct imx_audio_rpmsg *data = dev_get_drvdata(dev: &rpdev->dev); |
104 | |
105 | if (data->rpmsg_pdev) |
106 | platform_device_unregister(data->rpmsg_pdev); |
107 | |
108 | dev_info(&rpdev->dev, "audio rpmsg driver is removed\n" ); |
109 | } |
110 | |
111 | static struct rpmsg_device_id imx_audio_rpmsg_id_table[] = { |
112 | { .name = "rpmsg-audio-channel" }, |
113 | { .name = "rpmsg-micfil-channel" }, |
114 | { }, |
115 | }; |
116 | |
117 | static struct rpmsg_driver imx_audio_rpmsg_driver = { |
118 | .drv.name = "imx_audio_rpmsg" , |
119 | .id_table = imx_audio_rpmsg_id_table, |
120 | .probe = imx_audio_rpmsg_probe, |
121 | .callback = imx_audio_rpmsg_cb, |
122 | .remove = imx_audio_rpmsg_remove, |
123 | }; |
124 | |
125 | module_rpmsg_driver(imx_audio_rpmsg_driver); |
126 | |
127 | MODULE_DESCRIPTION("Freescale SoC Audio RPMSG interface" ); |
128 | MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>" ); |
129 | MODULE_ALIAS("platform:imx_audio_rpmsg" ); |
130 | MODULE_LICENSE("GPL v2" ); |
131 | |