meson-canvas.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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. };
  35. static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val)
  36. {
  37. writel_relaxed(val, canvas->reg_base + reg);
  38. }
  39. static u32 canvas_read(struct meson_canvas *canvas, u32 reg)
  40. {
  41. return readl_relaxed(canvas->reg_base + reg);
  42. }
  43. struct meson_canvas *meson_canvas_get(struct device *dev)
  44. {
  45. struct device_node *canvas_node;
  46. struct platform_device *canvas_pdev;
  47. canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
  48. if (!canvas_node)
  49. return ERR_PTR(-ENODEV);
  50. canvas_pdev = of_find_device_by_node(canvas_node);
  51. if (!canvas_pdev)
  52. return ERR_PTR(-EPROBE_DEFER);
  53. return dev_get_drvdata(&canvas_pdev->dev);
  54. }
  55. EXPORT_SYMBOL_GPL(meson_canvas_get);
  56. int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
  57. u32 addr, u32 stride, u32 height,
  58. unsigned int wrap,
  59. unsigned int blkmode,
  60. unsigned int endian)
  61. {
  62. unsigned long flags;
  63. spin_lock_irqsave(&canvas->lock, flags);
  64. if (!canvas->used[canvas_index]) {
  65. dev_err(canvas->dev,
  66. "Trying to setup non allocated canvas %u\n",
  67. canvas_index);
  68. spin_unlock_irqrestore(&canvas->lock, flags);
  69. return -EINVAL;
  70. }
  71. canvas_write(canvas, DMC_CAV_LUT_DATAL,
  72. ((addr + 7) >> 3) |
  73. (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
  74. canvas_write(canvas, DMC_CAV_LUT_DATAH,
  75. ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
  76. CANVAS_WIDTH_HBIT) |
  77. (height << CANVAS_HEIGHT_BIT) |
  78. (wrap << CANVAS_WRAP_BIT) |
  79. (blkmode << CANVAS_BLKMODE_BIT) |
  80. (endian << CANVAS_ENDIAN_BIT));
  81. canvas_write(canvas, DMC_CAV_LUT_ADDR,
  82. CANVAS_LUT_WR_EN | canvas_index);
  83. /* Force a read-back to make sure everything is flushed. */
  84. canvas_read(canvas, DMC_CAV_LUT_DATAH);
  85. spin_unlock_irqrestore(&canvas->lock, flags);
  86. return 0;
  87. }
  88. EXPORT_SYMBOL_GPL(meson_canvas_config);
  89. int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index)
  90. {
  91. int i;
  92. unsigned long flags;
  93. spin_lock_irqsave(&canvas->lock, flags);
  94. for (i = 0; i < NUM_CANVAS; ++i) {
  95. if (!canvas->used[i]) {
  96. canvas->used[i] = 1;
  97. spin_unlock_irqrestore(&canvas->lock, flags);
  98. *canvas_index = i;
  99. return 0;
  100. }
  101. }
  102. spin_unlock_irqrestore(&canvas->lock, flags);
  103. dev_err(canvas->dev, "No more canvas available\n");
  104. return -ENODEV;
  105. }
  106. EXPORT_SYMBOL_GPL(meson_canvas_alloc);
  107. int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index)
  108. {
  109. unsigned long flags;
  110. spin_lock_irqsave(&canvas->lock, flags);
  111. if (!canvas->used[canvas_index]) {
  112. dev_err(canvas->dev,
  113. "Trying to free unused canvas %u\n", canvas_index);
  114. spin_unlock_irqrestore(&canvas->lock, flags);
  115. return -EINVAL;
  116. }
  117. canvas->used[canvas_index] = 0;
  118. spin_unlock_irqrestore(&canvas->lock, flags);
  119. return 0;
  120. }
  121. EXPORT_SYMBOL_GPL(meson_canvas_free);
  122. static int meson_canvas_probe(struct platform_device *pdev)
  123. {
  124. struct resource *res;
  125. struct meson_canvas *canvas;
  126. struct device *dev = &pdev->dev;
  127. canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL);
  128. if (!canvas)
  129. return -ENOMEM;
  130. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  131. canvas->reg_base = devm_ioremap_resource(dev, res);
  132. if (IS_ERR(canvas->reg_base))
  133. return PTR_ERR(canvas->reg_base);
  134. canvas->dev = dev;
  135. spin_lock_init(&canvas->lock);
  136. dev_set_drvdata(dev, canvas);
  137. return 0;
  138. }
  139. static const struct of_device_id canvas_dt_match[] = {
  140. { .compatible = "amlogic,canvas" },
  141. {}
  142. };
  143. MODULE_DEVICE_TABLE(of, canvas_dt_match);
  144. static struct platform_driver meson_canvas_driver = {
  145. .probe = meson_canvas_probe,
  146. .driver = {
  147. .name = "amlogic-canvas",
  148. .of_match_table = canvas_dt_match,
  149. },
  150. };
  151. module_platform_driver(meson_canvas_driver);
  152. MODULE_DESCRIPTION("Amlogic Canvas driver");
  153. MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
  154. MODULE_LICENSE("GPL");