msu-sink.c 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * An example software sink buffer for Intel TH MSU.
  4. *
  5. * Copyright (C) 2019 Intel Corporation.
  6. */
  7. #include <linux/intel_th.h>
  8. #include <linux/module.h>
  9. #include <linux/slab.h>
  10. #include <linux/device.h>
  11. #include <linux/dma-mapping.h>
  12. #define MAX_SGTS 16
  13. struct msu_sink_private {
  14. struct device *dev;
  15. struct sg_table **sgts;
  16. unsigned int nr_sgts;
  17. };
  18. static void *msu_sink_assign(struct device *dev, int *mode)
  19. {
  20. struct msu_sink_private *priv;
  21. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  22. if (!priv)
  23. return NULL;
  24. priv->sgts = kcalloc(MAX_SGTS, sizeof(void *), GFP_KERNEL);
  25. if (!priv->sgts) {
  26. kfree(priv);
  27. return NULL;
  28. }
  29. priv->dev = dev;
  30. *mode = MSC_MODE_MULTI;
  31. return priv;
  32. }
  33. static void msu_sink_unassign(void *data)
  34. {
  35. struct msu_sink_private *priv = data;
  36. kfree(priv->sgts);
  37. kfree(priv);
  38. }
  39. /* See also: msc.c: __msc_buffer_win_alloc() */
  40. static int msu_sink_alloc_window(void *data, struct sg_table **sgt, size_t size)
  41. {
  42. struct msu_sink_private *priv = data;
  43. unsigned int nents;
  44. struct scatterlist *sg_ptr;
  45. void *block;
  46. int ret, i;
  47. if (priv->nr_sgts == MAX_SGTS)
  48. return -ENOMEM;
  49. nents = DIV_ROUND_UP(size, PAGE_SIZE);
  50. ret = sg_alloc_table(*sgt, nents, GFP_KERNEL);
  51. if (ret)
  52. return -ENOMEM;
  53. priv->sgts[priv->nr_sgts++] = *sgt;
  54. for_each_sg((*sgt)->sgl, sg_ptr, nents, i) {
  55. block = dma_alloc_coherent(priv->dev->parent->parent,
  56. PAGE_SIZE, &sg_dma_address(sg_ptr),
  57. GFP_KERNEL);
  58. if (!block)
  59. return -ENOMEM;
  60. sg_set_buf(sg_ptr, block, PAGE_SIZE);
  61. }
  62. return nents;
  63. }
  64. /* See also: msc.c: __msc_buffer_win_free() */
  65. static void msu_sink_free_window(void *data, struct sg_table *sgt)
  66. {
  67. struct msu_sink_private *priv = data;
  68. struct scatterlist *sg_ptr;
  69. int i;
  70. for_each_sg(sgt->sgl, sg_ptr, sgt->nents, i) {
  71. dma_free_coherent(priv->dev->parent->parent, PAGE_SIZE,
  72. sg_virt(sg_ptr), sg_dma_address(sg_ptr));
  73. }
  74. sg_free_table(sgt);
  75. priv->nr_sgts--;
  76. }
  77. static int msu_sink_ready(void *data, struct sg_table *sgt, size_t bytes)
  78. {
  79. struct msu_sink_private *priv = data;
  80. intel_th_msc_window_unlock(priv->dev, sgt);
  81. return 0;
  82. }
  83. static const struct msu_buffer sink_mbuf = {
  84. .name = "sink",
  85. .assign = msu_sink_assign,
  86. .unassign = msu_sink_unassign,
  87. .alloc_window = msu_sink_alloc_window,
  88. .free_window = msu_sink_free_window,
  89. .ready = msu_sink_ready,
  90. };
  91. module_intel_th_msu_buffer(sink_mbuf);
  92. MODULE_LICENSE("GPL v2");