meson_rdma.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2019 BayLibre, SAS
  4. * Author: Neil Armstrong <narmstrong@baylibre.com>
  5. */
  6. #include <linux/bitfield.h>
  7. #include <linux/dma-mapping.h>
  8. #include "meson_drv.h"
  9. #include "meson_registers.h"
  10. #include "meson_rdma.h"
  11. /*
  12. * The VPU embeds a "Register DMA" that can write a sequence of registers
  13. * on the VPU AHB bus, either manually or triggered by an internal IRQ
  14. * event like VSYNC or a line input counter.
  15. * The initial implementation handles a single channel (over 8), triggered
  16. * by the VSYNC irq and does not handle the RDMA irq.
  17. */
  18. #define RDMA_DESC_SIZE (sizeof(uint32_t) * 2)
  19. int meson_rdma_init(struct meson_drm *priv)
  20. {
  21. if (!priv->rdma.addr) {
  22. /* Allocate a PAGE buffer */
  23. priv->rdma.addr =
  24. dma_alloc_coherent(priv->dev, SZ_4K,
  25. &priv->rdma.addr_dma,
  26. GFP_KERNEL);
  27. if (!priv->rdma.addr)
  28. return -ENOMEM;
  29. }
  30. priv->rdma.offset = 0;
  31. writel_relaxed(RDMA_CTRL_SW_RESET,
  32. priv->io_base + _REG(RDMA_CTRL));
  33. writel_relaxed(RDMA_DEFAULT_CONFIG |
  34. FIELD_PREP(RDMA_CTRL_AHB_WR_BURST, 3) |
  35. FIELD_PREP(RDMA_CTRL_AHB_RD_BURST, 0),
  36. priv->io_base + _REG(RDMA_CTRL));
  37. return 0;
  38. }
  39. void meson_rdma_free(struct meson_drm *priv)
  40. {
  41. if (!priv->rdma.addr && !priv->rdma.addr_dma)
  42. return;
  43. meson_rdma_stop(priv);
  44. dma_free_coherent(priv->dev, SZ_4K,
  45. priv->rdma.addr, priv->rdma.addr_dma);
  46. priv->rdma.addr = NULL;
  47. priv->rdma.addr_dma = (dma_addr_t)0;
  48. }
  49. void meson_rdma_setup(struct meson_drm *priv)
  50. {
  51. /* Channel 1: Write Flag, No Address Increment */
  52. writel_bits_relaxed(RDMA_ACCESS_RW_FLAG_CHAN1 |
  53. RDMA_ACCESS_ADDR_INC_CHAN1,
  54. RDMA_ACCESS_RW_FLAG_CHAN1,
  55. priv->io_base + _REG(RDMA_ACCESS_AUTO));
  56. }
  57. void meson_rdma_stop(struct meson_drm *priv)
  58. {
  59. writel_bits_relaxed(RDMA_IRQ_CLEAR_CHAN1,
  60. RDMA_IRQ_CLEAR_CHAN1,
  61. priv->io_base + _REG(RDMA_CTRL));
  62. /* Stop Channel 1 */
  63. writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
  64. FIELD_PREP(RDMA_ACCESS_ADDR_INC_CHAN1,
  65. RDMA_ACCESS_TRIGGER_STOP),
  66. priv->io_base + _REG(RDMA_ACCESS_AUTO));
  67. }
  68. void meson_rdma_reset(struct meson_drm *priv)
  69. {
  70. meson_rdma_stop(priv);
  71. priv->rdma.offset = 0;
  72. }
  73. static void meson_rdma_writel(struct meson_drm *priv, uint32_t val,
  74. uint32_t reg)
  75. {
  76. if (priv->rdma.offset >= (SZ_4K / RDMA_DESC_SIZE)) {
  77. dev_warn_once(priv->dev, "%s: overflow\n", __func__);
  78. return;
  79. }
  80. priv->rdma.addr[priv->rdma.offset++] = reg;
  81. priv->rdma.addr[priv->rdma.offset++] = val;
  82. }
  83. /*
  84. * This will add the register to the RDMA buffer and write it to the
  85. * hardware at the same time.
  86. * When meson_rdma_flush is called, the RDMA will replay the register
  87. * writes in order.
  88. */
  89. void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg)
  90. {
  91. meson_rdma_writel(priv, val, reg);
  92. writel_relaxed(val, priv->io_base + _REG(reg));
  93. }
  94. void meson_rdma_flush(struct meson_drm *priv)
  95. {
  96. meson_rdma_stop(priv);
  97. /* Start of Channel 1 register writes buffer */
  98. writel(priv->rdma.addr_dma,
  99. priv->io_base + _REG(RDMA_AHB_START_ADDR_1));
  100. /* Last byte on Channel 1 register writes buffer */
  101. writel(priv->rdma.addr_dma + (priv->rdma.offset * RDMA_DESC_SIZE) - 1,
  102. priv->io_base + _REG(RDMA_AHB_END_ADDR_1));
  103. /* Trigger Channel 1 on VSYNC event */
  104. writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
  105. FIELD_PREP(RDMA_ACCESS_TRIGGER_CHAN1,
  106. RDMA_ACCESS_TRIGGER_VSYNC),
  107. priv->io_base + _REG(RDMA_ACCESS_AUTO));
  108. priv->rdma.offset = 0;
  109. }