imx-dsp.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright 2019 NXP
  4. * Author: Daniel Baluta <[email protected]>
  5. *
  6. * Implementation of the DSP IPC interface (host side)
  7. */
  8. #include <linux/firmware/imx/dsp.h>
  9. #include <linux/kernel.h>
  10. #include <linux/mailbox_client.h>
  11. #include <linux/module.h>
  12. #include <linux/of_platform.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/slab.h>
  15. /*
  16. * imx_dsp_ring_doorbell - triggers an interrupt on the other side (DSP)
  17. *
  18. * @dsp: DSP IPC handle
  19. * @chan_idx: index of the channel where to trigger the interrupt
  20. *
  21. * Returns non-negative value for success, negative value for error
  22. */
  23. int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, unsigned int idx)
  24. {
  25. int ret;
  26. struct imx_dsp_chan *dsp_chan;
  27. if (idx >= DSP_MU_CHAN_NUM)
  28. return -EINVAL;
  29. dsp_chan = &ipc->chans[idx];
  30. ret = mbox_send_message(dsp_chan->ch, NULL);
  31. if (ret < 0)
  32. return ret;
  33. return 0;
  34. }
  35. EXPORT_SYMBOL(imx_dsp_ring_doorbell);
  36. /*
  37. * imx_dsp_handle_rx - rx callback used by imx mailbox
  38. *
  39. * @c: mbox client
  40. * @msg: message received
  41. *
  42. * Users of DSP IPC will need to privde handle_reply and handle_request
  43. * callbacks.
  44. */
  45. static void imx_dsp_handle_rx(struct mbox_client *c, void *msg)
  46. {
  47. struct imx_dsp_chan *chan = container_of(c, struct imx_dsp_chan, cl);
  48. if (chan->idx == 0) {
  49. chan->ipc->ops->handle_reply(chan->ipc);
  50. } else {
  51. chan->ipc->ops->handle_request(chan->ipc);
  52. imx_dsp_ring_doorbell(chan->ipc, 1);
  53. }
  54. }
  55. struct mbox_chan *imx_dsp_request_channel(struct imx_dsp_ipc *dsp_ipc, int idx)
  56. {
  57. struct imx_dsp_chan *dsp_chan;
  58. if (idx >= DSP_MU_CHAN_NUM)
  59. return ERR_PTR(-EINVAL);
  60. dsp_chan = &dsp_ipc->chans[idx];
  61. dsp_chan->ch = mbox_request_channel_byname(&dsp_chan->cl, dsp_chan->name);
  62. return dsp_chan->ch;
  63. }
  64. EXPORT_SYMBOL(imx_dsp_request_channel);
  65. void imx_dsp_free_channel(struct imx_dsp_ipc *dsp_ipc, int idx)
  66. {
  67. struct imx_dsp_chan *dsp_chan;
  68. if (idx >= DSP_MU_CHAN_NUM)
  69. return;
  70. dsp_chan = &dsp_ipc->chans[idx];
  71. mbox_free_channel(dsp_chan->ch);
  72. }
  73. EXPORT_SYMBOL(imx_dsp_free_channel);
  74. static int imx_dsp_setup_channels(struct imx_dsp_ipc *dsp_ipc)
  75. {
  76. struct device *dev = dsp_ipc->dev;
  77. struct imx_dsp_chan *dsp_chan;
  78. struct mbox_client *cl;
  79. char *chan_name;
  80. int ret;
  81. int i, j;
  82. for (i = 0; i < DSP_MU_CHAN_NUM; i++) {
  83. if (i < 2)
  84. chan_name = kasprintf(GFP_KERNEL, "txdb%d", i);
  85. else
  86. chan_name = kasprintf(GFP_KERNEL, "rxdb%d", i - 2);
  87. if (!chan_name)
  88. return -ENOMEM;
  89. dsp_chan = &dsp_ipc->chans[i];
  90. dsp_chan->name = chan_name;
  91. cl = &dsp_chan->cl;
  92. cl->dev = dev;
  93. cl->tx_block = false;
  94. cl->knows_txdone = true;
  95. cl->rx_callback = imx_dsp_handle_rx;
  96. dsp_chan->ipc = dsp_ipc;
  97. dsp_chan->idx = i % 2;
  98. dsp_chan->ch = mbox_request_channel_byname(cl, chan_name);
  99. if (IS_ERR(dsp_chan->ch)) {
  100. ret = PTR_ERR(dsp_chan->ch);
  101. if (ret != -EPROBE_DEFER)
  102. dev_err(dev, "Failed to request mbox chan %s ret %d\n",
  103. chan_name, ret);
  104. kfree(dsp_chan->name);
  105. goto out;
  106. }
  107. dev_dbg(dev, "request mbox chan %s\n", chan_name);
  108. }
  109. return 0;
  110. out:
  111. for (j = 0; j < i; j++) {
  112. dsp_chan = &dsp_ipc->chans[j];
  113. mbox_free_channel(dsp_chan->ch);
  114. kfree(dsp_chan->name);
  115. }
  116. return ret;
  117. }
  118. static int imx_dsp_probe(struct platform_device *pdev)
  119. {
  120. struct device *dev = &pdev->dev;
  121. struct imx_dsp_ipc *dsp_ipc;
  122. int ret;
  123. device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
  124. dsp_ipc = devm_kzalloc(dev, sizeof(*dsp_ipc), GFP_KERNEL);
  125. if (!dsp_ipc)
  126. return -ENOMEM;
  127. dsp_ipc->dev = dev;
  128. dev_set_drvdata(dev, dsp_ipc);
  129. ret = imx_dsp_setup_channels(dsp_ipc);
  130. if (ret < 0)
  131. return ret;
  132. dev_info(dev, "NXP i.MX DSP IPC initialized\n");
  133. return 0;
  134. }
  135. static int imx_dsp_remove(struct platform_device *pdev)
  136. {
  137. struct imx_dsp_chan *dsp_chan;
  138. struct imx_dsp_ipc *dsp_ipc;
  139. int i;
  140. dsp_ipc = dev_get_drvdata(&pdev->dev);
  141. for (i = 0; i < DSP_MU_CHAN_NUM; i++) {
  142. dsp_chan = &dsp_ipc->chans[i];
  143. mbox_free_channel(dsp_chan->ch);
  144. kfree(dsp_chan->name);
  145. }
  146. return 0;
  147. }
  148. static struct platform_driver imx_dsp_driver = {
  149. .driver = {
  150. .name = "imx-dsp",
  151. },
  152. .probe = imx_dsp_probe,
  153. .remove = imx_dsp_remove,
  154. };
  155. builtin_platform_driver(imx_dsp_driver);
  156. MODULE_AUTHOR("Daniel Baluta <[email protected]>");
  157. MODULE_DESCRIPTION("IMX DSP IPC protocol driver");
  158. MODULE_LICENSE("GPL v2");