meson-canvas.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2018 BayLibre, SAS
  4. * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
  5. * Copyright (C) 2014 Endless Mobile
  6. */
  7. #include <linux/kernel.h>
  8. #include <linux/mfd/syscon.h>
  9. #include <linux/module.h>
  10. #include <linux/regmap.h>
  11. #include <linux/soc/amlogic/meson-canvas.h>
  12. #include <linux/of_address.h>
  13. #include <linux/of_platform.h>
  14. #include <linux/io.h>
  15. #define NUM_CANVAS 256
  16. /* DMC Registers */
  17. #define DMC_CAV_LUT_DATAL 0x00
  18. #define CANVAS_WIDTH_LBIT 29
  19. #define CANVAS_WIDTH_LWID 3
  20. #define DMC_CAV_LUT_DATAH 0x04
  21. #define CANVAS_WIDTH_HBIT 0
  22. #define CANVAS_HEIGHT_BIT 9
  23. #define CANVAS_WRAP_BIT 22
  24. #define CANVAS_BLKMODE_BIT 24
  25. #define CANVAS_ENDIAN_BIT 26
  26. #define DMC_CAV_LUT_ADDR 0x08
  27. #define CANVAS_LUT_WR_EN BIT(9)
  28. #define CANVAS_LUT_RD_EN BIT(8)
  29. struct meson_canvas {
  30. struct device *dev;
  31. void __iomem *reg_base;
  32. spinlock_t lock; /* canvas device lock */
  33. u8 used[NUM_CANVAS];
  34. bool supports_endianness;
  35. };
  36. static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val)
  37. {
  38. writel_relaxed(val, canvas->reg_base + reg);
  39. }
  40. static u32 canvas_read(struct meson_canvas *canvas, u32 reg)
  41. {
  42. return readl_relaxed(canvas->reg_base + reg);
  43. }
  44. struct meson_canvas *meson_canvas_get(struct device *dev)
  45. {
  46. struct device_node *canvas_node;
  47. struct platform_device *canvas_pdev;
  48. struct meson_canvas *canvas;
  49. canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
  50. if (!canvas_node)
  51. return ERR_PTR(-ENODEV);
  52. canvas_pdev = of_find_device_by_node(canvas_node);
  53. if (!canvas_pdev) {
  54. of_node_put(canvas_node);
  55. return ERR_PTR(-EPROBE_DEFER);
  56. }
  57. of_node_put(canvas_node);
  58. /*
  59. * If priv is NULL, it's probably because the canvas hasn't
  60. * properly initialized. Bail out with -EINVAL because, in the
  61. * current state, this driver probe cannot return -EPROBE_DEFER
  62. */
  63. canvas = dev_get_drvdata(&canvas_pdev->dev);
  64. if (!canvas) {
  65. put_device(&canvas_pdev->dev);
  66. return ERR_PTR(-EINVAL);
  67. }
  68. return canvas;
  69. }
  70. EXPORT_SYMBOL_GPL(meson_canvas_get);
  71. int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
  72. u32 addr, u32 stride, u32 height,
  73. unsigned int wrap,
  74. unsigned int blkmode,
  75. unsigned int endian)
  76. {
  77. unsigned long flags;
  78. if (endian && !canvas->supports_endianness) {
  79. dev_err(canvas->dev,
  80. "Endianness is not supported on this SoC\n");
  81. return -EINVAL;
  82. }
  83. spin_lock_irqsave(&canvas->lock, flags);
  84. if (!canvas->used[canvas_index]) {
  85. dev_err(canvas->dev,
  86. "Trying to setup non allocated canvas %u\n",
  87. canvas_index);
  88. spin_unlock_irqrestore(&canvas->lock, flags);
  89. return -EINVAL;
  90. }
  91. canvas_write(canvas, DMC_CAV_LUT_DATAL,
  92. ((addr + 7) >> 3) |
  93. (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
  94. canvas_write(canvas, DMC_CAV_LUT_DATAH,
  95. ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
  96. CANVAS_WIDTH_HBIT) |
  97. (height << CANVAS_HEIGHT_BIT) |
  98. (wrap << CANVAS_WRAP_BIT) |
  99. (blkmode << CANVAS_BLKMODE_BIT) |
  100. (endian << CANVAS_ENDIAN_BIT));
  101. canvas_write(canvas, DMC_CAV_LUT_ADDR,
  102. CANVAS_LUT_WR_EN | canvas_index);
  103. /* Force a read-back to make sure everything is flushed. */
  104. canvas_read(canvas, DMC_CAV_LUT_DATAH);
  105. spin_unlock_irqrestore(&canvas->lock, flags);
  106. return 0;
  107. }
  108. EXPORT_SYMBOL_GPL(meson_canvas_config);
  109. int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index)
  110. {
  111. int i;
  112. unsigned long flags;
  113. spin_lock_irqsave(&canvas->lock, flags);
  114. for (i = 0; i < NUM_CANVAS; ++i) {
  115. if (!canvas->used[i]) {
  116. canvas->used[i] = 1;
  117. spin_unlock_irqrestore(&canvas->lock, flags);
  118. *canvas_index = i;
  119. return 0;
  120. }
  121. }
  122. spin_unlock_irqrestore(&canvas->lock, flags);
  123. dev_err(canvas->dev, "No more canvas available\n");
  124. return -ENODEV;
  125. }
  126. EXPORT_SYMBOL_GPL(meson_canvas_alloc);
  127. int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index)
  128. {
  129. unsigned long flags;
  130. spin_lock_irqsave(&canvas->lock, flags);
  131. if (!canvas->used[canvas_index]) {
  132. dev_err(canvas->dev,
  133. "Trying to free unused canvas %u\n", canvas_index);
  134. spin_unlock_irqrestore(&canvas->lock, flags);
  135. return -EINVAL;
  136. }
  137. canvas->used[canvas_index] = 0;
  138. spin_unlock_irqrestore(&canvas->lock, flags);
  139. return 0;
  140. }
  141. EXPORT_SYMBOL_GPL(meson_canvas_free);
  142. static int meson_canvas_probe(struct platform_device *pdev)
  143. {
  144. struct resource *res;
  145. struct meson_canvas *canvas;
  146. struct device *dev = &pdev->dev;
  147. canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL);
  148. if (!canvas)
  149. return -ENOMEM;
  150. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  151. canvas->reg_base = devm_ioremap_resource(dev, res);
  152. if (IS_ERR(canvas->reg_base))
  153. return PTR_ERR(canvas->reg_base);
  154. canvas->supports_endianness = of_device_get_match_data(dev);
  155. canvas->dev = dev;
  156. spin_lock_init(&canvas->lock);
  157. dev_set_drvdata(dev, canvas);
  158. return 0;
  159. }
  160. static const struct of_device_id canvas_dt_match[] = {
  161. { .compatible = "amlogic,meson8-canvas", .data = (void *)false, },
  162. { .compatible = "amlogic,meson8b-canvas", .data = (void *)false, },
  163. { .compatible = "amlogic,meson8m2-canvas", .data = (void *)false, },
  164. { .compatible = "amlogic,canvas", .data = (void *)true, },
  165. {}
  166. };
  167. MODULE_DEVICE_TABLE(of, canvas_dt_match);
  168. static struct platform_driver meson_canvas_driver = {
  169. .probe = meson_canvas_probe,
  170. .driver = {
  171. .name = "amlogic-canvas",
  172. .of_match_table = canvas_dt_match,
  173. },
  174. };
  175. module_platform_driver(meson_canvas_driver);
  176. MODULE_DESCRIPTION("Amlogic Canvas driver");
  177. MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
  178. MODULE_LICENSE("GPL");