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(&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(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(&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(&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(&rpdev->dev, sizeof(*data), GFP_KERNEL); 83 if (!data) 84 return -ENOMEM; 85 86 dev_set_drvdata(&rpdev->dev, data); 87 88 /* Register platform driver for rpmsg routine */ 89 data->rpmsg_pdev = platform_device_register_data(&rpdev->dev, 90 IMX_PCM_DRV_NAME, 91 PLATFORM_DEVID_NONE, 92 NULL, 0); 93 if (IS_ERR(data->rpmsg_pdev)) { 94 dev_err(&rpdev->dev, "failed to register rpmsg platform.\n"); 95 ret = PTR_ERR(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(&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 { }, 114 }; 115 116 static struct rpmsg_driver imx_audio_rpmsg_driver = { 117 .drv.name = "imx_audio_rpmsg", 118 .drv.owner = THIS_MODULE, 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 static int __init imx_audio_rpmsg_init(void) 126 { 127 return register_rpmsg_driver(&imx_audio_rpmsg_driver); 128 } 129 130 static void __exit imx_audio_rpmsg_exit(void) 131 { 132 unregister_rpmsg_driver(&imx_audio_rpmsg_driver); 133 } 134 module_init(imx_audio_rpmsg_init); 135 module_exit(imx_audio_rpmsg_exit); 136 137 MODULE_DESCRIPTION("Freescale SoC Audio RPMSG interface"); 138 MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>"); 139 MODULE_ALIAS("platform:imx_audio_rpmsg"); 140 MODULE_LICENSE("GPL v2"); 141