0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/kernel.h>
0009 #include <linux/mfd/syscon.h>
0010 #include <linux/module.h>
0011 #include <linux/regmap.h>
0012 #include <linux/soc/amlogic/meson-canvas.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_platform.h>
0015 #include <linux/io.h>
0016
0017 #define NUM_CANVAS 256
0018
0019
0020 #define DMC_CAV_LUT_DATAL 0x00
0021 #define CANVAS_WIDTH_LBIT 29
0022 #define CANVAS_WIDTH_LWID 3
0023 #define DMC_CAV_LUT_DATAH 0x04
0024 #define CANVAS_WIDTH_HBIT 0
0025 #define CANVAS_HEIGHT_BIT 9
0026 #define CANVAS_WRAP_BIT 22
0027 #define CANVAS_BLKMODE_BIT 24
0028 #define CANVAS_ENDIAN_BIT 26
0029 #define DMC_CAV_LUT_ADDR 0x08
0030 #define CANVAS_LUT_WR_EN BIT(9)
0031 #define CANVAS_LUT_RD_EN BIT(8)
0032
0033 struct meson_canvas {
0034 struct device *dev;
0035 void __iomem *reg_base;
0036 spinlock_t lock;
0037 u8 used[NUM_CANVAS];
0038 bool supports_endianness;
0039 };
0040
0041 static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val)
0042 {
0043 writel_relaxed(val, canvas->reg_base + reg);
0044 }
0045
0046 static u32 canvas_read(struct meson_canvas *canvas, u32 reg)
0047 {
0048 return readl_relaxed(canvas->reg_base + reg);
0049 }
0050
0051 struct meson_canvas *meson_canvas_get(struct device *dev)
0052 {
0053 struct device_node *canvas_node;
0054 struct platform_device *canvas_pdev;
0055 struct meson_canvas *canvas;
0056
0057 canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
0058 if (!canvas_node)
0059 return ERR_PTR(-ENODEV);
0060
0061 canvas_pdev = of_find_device_by_node(canvas_node);
0062 if (!canvas_pdev) {
0063 of_node_put(canvas_node);
0064 return ERR_PTR(-EPROBE_DEFER);
0065 }
0066
0067 of_node_put(canvas_node);
0068
0069
0070
0071
0072
0073
0074 canvas = dev_get_drvdata(&canvas_pdev->dev);
0075 if (!canvas) {
0076 put_device(&canvas_pdev->dev);
0077 return ERR_PTR(-EINVAL);
0078 }
0079
0080 return canvas;
0081 }
0082 EXPORT_SYMBOL_GPL(meson_canvas_get);
0083
0084 int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
0085 u32 addr, u32 stride, u32 height,
0086 unsigned int wrap,
0087 unsigned int blkmode,
0088 unsigned int endian)
0089 {
0090 unsigned long flags;
0091
0092 if (endian && !canvas->supports_endianness) {
0093 dev_err(canvas->dev,
0094 "Endianness is not supported on this SoC\n");
0095 return -EINVAL;
0096 }
0097
0098 spin_lock_irqsave(&canvas->lock, flags);
0099 if (!canvas->used[canvas_index]) {
0100 dev_err(canvas->dev,
0101 "Trying to setup non allocated canvas %u\n",
0102 canvas_index);
0103 spin_unlock_irqrestore(&canvas->lock, flags);
0104 return -EINVAL;
0105 }
0106
0107 canvas_write(canvas, DMC_CAV_LUT_DATAL,
0108 ((addr + 7) >> 3) |
0109 (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
0110
0111 canvas_write(canvas, DMC_CAV_LUT_DATAH,
0112 ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
0113 CANVAS_WIDTH_HBIT) |
0114 (height << CANVAS_HEIGHT_BIT) |
0115 (wrap << CANVAS_WRAP_BIT) |
0116 (blkmode << CANVAS_BLKMODE_BIT) |
0117 (endian << CANVAS_ENDIAN_BIT));
0118
0119 canvas_write(canvas, DMC_CAV_LUT_ADDR,
0120 CANVAS_LUT_WR_EN | canvas_index);
0121
0122
0123 canvas_read(canvas, DMC_CAV_LUT_DATAH);
0124 spin_unlock_irqrestore(&canvas->lock, flags);
0125
0126 return 0;
0127 }
0128 EXPORT_SYMBOL_GPL(meson_canvas_config);
0129
0130 int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index)
0131 {
0132 int i;
0133 unsigned long flags;
0134
0135 spin_lock_irqsave(&canvas->lock, flags);
0136 for (i = 0; i < NUM_CANVAS; ++i) {
0137 if (!canvas->used[i]) {
0138 canvas->used[i] = 1;
0139 spin_unlock_irqrestore(&canvas->lock, flags);
0140 *canvas_index = i;
0141 return 0;
0142 }
0143 }
0144 spin_unlock_irqrestore(&canvas->lock, flags);
0145
0146 dev_err(canvas->dev, "No more canvas available\n");
0147 return -ENODEV;
0148 }
0149 EXPORT_SYMBOL_GPL(meson_canvas_alloc);
0150
0151 int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index)
0152 {
0153 unsigned long flags;
0154
0155 spin_lock_irqsave(&canvas->lock, flags);
0156 if (!canvas->used[canvas_index]) {
0157 dev_err(canvas->dev,
0158 "Trying to free unused canvas %u\n", canvas_index);
0159 spin_unlock_irqrestore(&canvas->lock, flags);
0160 return -EINVAL;
0161 }
0162 canvas->used[canvas_index] = 0;
0163 spin_unlock_irqrestore(&canvas->lock, flags);
0164
0165 return 0;
0166 }
0167 EXPORT_SYMBOL_GPL(meson_canvas_free);
0168
0169 static int meson_canvas_probe(struct platform_device *pdev)
0170 {
0171 struct meson_canvas *canvas;
0172 struct device *dev = &pdev->dev;
0173
0174 canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL);
0175 if (!canvas)
0176 return -ENOMEM;
0177
0178 canvas->reg_base = devm_platform_ioremap_resource(pdev, 0);
0179 if (IS_ERR(canvas->reg_base))
0180 return PTR_ERR(canvas->reg_base);
0181
0182 canvas->supports_endianness = of_device_get_match_data(dev);
0183
0184 canvas->dev = dev;
0185 spin_lock_init(&canvas->lock);
0186 dev_set_drvdata(dev, canvas);
0187
0188 return 0;
0189 }
0190
0191 static const struct of_device_id canvas_dt_match[] = {
0192 { .compatible = "amlogic,meson8-canvas", .data = (void *)false, },
0193 { .compatible = "amlogic,meson8b-canvas", .data = (void *)false, },
0194 { .compatible = "amlogic,meson8m2-canvas", .data = (void *)false, },
0195 { .compatible = "amlogic,canvas", .data = (void *)true, },
0196 {}
0197 };
0198 MODULE_DEVICE_TABLE(of, canvas_dt_match);
0199
0200 static struct platform_driver meson_canvas_driver = {
0201 .probe = meson_canvas_probe,
0202 .driver = {
0203 .name = "amlogic-canvas",
0204 .of_match_table = canvas_dt_match,
0205 },
0206 };
0207 module_platform_driver(meson_canvas_driver);
0208
0209 MODULE_DESCRIPTION("Amlogic Canvas driver");
0210 MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
0211 MODULE_LICENSE("GPL");