imx-dsp.c 4.2 KB

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