1d4983983SMaxime Jourdan // SPDX-License-Identifier: GPL-2.0+
2d4983983SMaxime Jourdan /*
3d4983983SMaxime Jourdan * Copyright (C) 2018 BayLibre, SAS
4d4983983SMaxime Jourdan * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
5d4983983SMaxime Jourdan * Copyright (C) 2014 Endless Mobile
6d4983983SMaxime Jourdan */
7d4983983SMaxime Jourdan
8d4983983SMaxime Jourdan #include <linux/kernel.h>
9d4983983SMaxime Jourdan #include <linux/mfd/syscon.h>
10d4983983SMaxime Jourdan #include <linux/module.h>
11*584ed6d4SRob Herring #include <linux/platform_device.h>
12d4983983SMaxime Jourdan #include <linux/regmap.h>
13d4983983SMaxime Jourdan #include <linux/soc/amlogic/meson-canvas.h>
14d4983983SMaxime Jourdan #include <linux/of_address.h>
15d4983983SMaxime Jourdan #include <linux/of_platform.h>
16d4983983SMaxime Jourdan #include <linux/io.h>
17d4983983SMaxime Jourdan
18d4983983SMaxime Jourdan #define NUM_CANVAS 256
19d4983983SMaxime Jourdan
20d4983983SMaxime Jourdan /* DMC Registers */
21d4983983SMaxime Jourdan #define DMC_CAV_LUT_DATAL 0x00
22d4983983SMaxime Jourdan #define CANVAS_WIDTH_LBIT 29
23d4983983SMaxime Jourdan #define CANVAS_WIDTH_LWID 3
24d4983983SMaxime Jourdan #define DMC_CAV_LUT_DATAH 0x04
25d4983983SMaxime Jourdan #define CANVAS_WIDTH_HBIT 0
26d4983983SMaxime Jourdan #define CANVAS_HEIGHT_BIT 9
27d4983983SMaxime Jourdan #define CANVAS_WRAP_BIT 22
28d4983983SMaxime Jourdan #define CANVAS_BLKMODE_BIT 24
29d4983983SMaxime Jourdan #define CANVAS_ENDIAN_BIT 26
30d4983983SMaxime Jourdan #define DMC_CAV_LUT_ADDR 0x08
31d4983983SMaxime Jourdan #define CANVAS_LUT_WR_EN BIT(9)
32d4983983SMaxime Jourdan #define CANVAS_LUT_RD_EN BIT(8)
33d4983983SMaxime Jourdan
34d4983983SMaxime Jourdan struct meson_canvas {
35d4983983SMaxime Jourdan struct device *dev;
36d4983983SMaxime Jourdan void __iomem *reg_base;
37d4983983SMaxime Jourdan spinlock_t lock; /* canvas device lock */
38d4983983SMaxime Jourdan u8 used[NUM_CANVAS];
399a98fdf5SMartin Blumenstingl bool supports_endianness;
40d4983983SMaxime Jourdan };
41d4983983SMaxime Jourdan
canvas_write(struct meson_canvas * canvas,u32 reg,u32 val)42d4983983SMaxime Jourdan static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val)
43d4983983SMaxime Jourdan {
44d4983983SMaxime Jourdan writel_relaxed(val, canvas->reg_base + reg);
45d4983983SMaxime Jourdan }
46d4983983SMaxime Jourdan
canvas_read(struct meson_canvas * canvas,u32 reg)47d4983983SMaxime Jourdan static u32 canvas_read(struct meson_canvas *canvas, u32 reg)
48d4983983SMaxime Jourdan {
49d4983983SMaxime Jourdan return readl_relaxed(canvas->reg_base + reg);
50d4983983SMaxime Jourdan }
51d4983983SMaxime Jourdan
meson_canvas_get(struct device * dev)52d4983983SMaxime Jourdan struct meson_canvas *meson_canvas_get(struct device *dev)
53d4983983SMaxime Jourdan {
54d4983983SMaxime Jourdan struct device_node *canvas_node;
55d4983983SMaxime Jourdan struct platform_device *canvas_pdev;
56382f8be0SNeil Armstrong struct meson_canvas *canvas;
57d4983983SMaxime Jourdan
58d4983983SMaxime Jourdan canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
59d4983983SMaxime Jourdan if (!canvas_node)
60d4983983SMaxime Jourdan return ERR_PTR(-ENODEV);
61d4983983SMaxime Jourdan
62d4983983SMaxime Jourdan canvas_pdev = of_find_device_by_node(canvas_node);
6399e5a8dfSwen yang if (!canvas_pdev) {
6499e5a8dfSwen yang of_node_put(canvas_node);
65d4983983SMaxime Jourdan return ERR_PTR(-EPROBE_DEFER);
6699e5a8dfSwen yang }
67d4983983SMaxime Jourdan
6899e5a8dfSwen yang of_node_put(canvas_node);
69382f8be0SNeil Armstrong
70382f8be0SNeil Armstrong /*
71382f8be0SNeil Armstrong * If priv is NULL, it's probably because the canvas hasn't
72382f8be0SNeil Armstrong * properly initialized. Bail out with -EINVAL because, in the
73382f8be0SNeil Armstrong * current state, this driver probe cannot return -EPROBE_DEFER
74382f8be0SNeil Armstrong */
75382f8be0SNeil Armstrong canvas = dev_get_drvdata(&canvas_pdev->dev);
7628f851e6SYu Kuai if (!canvas) {
7728f851e6SYu Kuai put_device(&canvas_pdev->dev);
78382f8be0SNeil Armstrong return ERR_PTR(-EINVAL);
7928f851e6SYu Kuai }
80382f8be0SNeil Armstrong
81382f8be0SNeil Armstrong return canvas;
82d4983983SMaxime Jourdan }
83d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_get);
84d4983983SMaxime Jourdan
meson_canvas_config(struct meson_canvas * canvas,u8 canvas_index,u32 addr,u32 stride,u32 height,unsigned int wrap,unsigned int blkmode,unsigned int endian)85d4983983SMaxime Jourdan int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
86d4983983SMaxime Jourdan u32 addr, u32 stride, u32 height,
87d4983983SMaxime Jourdan unsigned int wrap,
88d4983983SMaxime Jourdan unsigned int blkmode,
89d4983983SMaxime Jourdan unsigned int endian)
90d4983983SMaxime Jourdan {
91d4983983SMaxime Jourdan unsigned long flags;
92d4983983SMaxime Jourdan
939a98fdf5SMartin Blumenstingl if (endian && !canvas->supports_endianness) {
949a98fdf5SMartin Blumenstingl dev_err(canvas->dev,
959a98fdf5SMartin Blumenstingl "Endianness is not supported on this SoC\n");
969a98fdf5SMartin Blumenstingl return -EINVAL;
979a98fdf5SMartin Blumenstingl }
989a98fdf5SMartin Blumenstingl
99d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags);
100d4983983SMaxime Jourdan if (!canvas->used[canvas_index]) {
101d4983983SMaxime Jourdan dev_err(canvas->dev,
102d4983983SMaxime Jourdan "Trying to setup non allocated canvas %u\n",
103d4983983SMaxime Jourdan canvas_index);
104d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags);
105d4983983SMaxime Jourdan return -EINVAL;
106d4983983SMaxime Jourdan }
107d4983983SMaxime Jourdan
108d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_DATAL,
109d4983983SMaxime Jourdan ((addr + 7) >> 3) |
110d4983983SMaxime Jourdan (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
111d4983983SMaxime Jourdan
112d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_DATAH,
113d4983983SMaxime Jourdan ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
114d4983983SMaxime Jourdan CANVAS_WIDTH_HBIT) |
115d4983983SMaxime Jourdan (height << CANVAS_HEIGHT_BIT) |
116d4983983SMaxime Jourdan (wrap << CANVAS_WRAP_BIT) |
117d4983983SMaxime Jourdan (blkmode << CANVAS_BLKMODE_BIT) |
118d4983983SMaxime Jourdan (endian << CANVAS_ENDIAN_BIT));
119d4983983SMaxime Jourdan
120d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_ADDR,
121d4983983SMaxime Jourdan CANVAS_LUT_WR_EN | canvas_index);
122d4983983SMaxime Jourdan
123d4983983SMaxime Jourdan /* Force a read-back to make sure everything is flushed. */
124d4983983SMaxime Jourdan canvas_read(canvas, DMC_CAV_LUT_DATAH);
125d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags);
126d4983983SMaxime Jourdan
127d4983983SMaxime Jourdan return 0;
128d4983983SMaxime Jourdan }
129d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_config);
130d4983983SMaxime Jourdan
meson_canvas_alloc(struct meson_canvas * canvas,u8 * canvas_index)131d4983983SMaxime Jourdan int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index)
132d4983983SMaxime Jourdan {
133d4983983SMaxime Jourdan int i;
134d4983983SMaxime Jourdan unsigned long flags;
135d4983983SMaxime Jourdan
136d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags);
137d4983983SMaxime Jourdan for (i = 0; i < NUM_CANVAS; ++i) {
138d4983983SMaxime Jourdan if (!canvas->used[i]) {
139d4983983SMaxime Jourdan canvas->used[i] = 1;
140d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags);
141d4983983SMaxime Jourdan *canvas_index = i;
142d4983983SMaxime Jourdan return 0;
143d4983983SMaxime Jourdan }
144d4983983SMaxime Jourdan }
145d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags);
146d4983983SMaxime Jourdan
147d4983983SMaxime Jourdan dev_err(canvas->dev, "No more canvas available\n");
148d4983983SMaxime Jourdan return -ENODEV;
149d4983983SMaxime Jourdan }
150d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_alloc);
151d4983983SMaxime Jourdan
meson_canvas_free(struct meson_canvas * canvas,u8 canvas_index)152d4983983SMaxime Jourdan int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index)
153d4983983SMaxime Jourdan {
154d4983983SMaxime Jourdan unsigned long flags;
155d4983983SMaxime Jourdan
156d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags);
157d4983983SMaxime Jourdan if (!canvas->used[canvas_index]) {
158d4983983SMaxime Jourdan dev_err(canvas->dev,
159d4983983SMaxime Jourdan "Trying to free unused canvas %u\n", canvas_index);
160d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags);
161d4983983SMaxime Jourdan return -EINVAL;
162d4983983SMaxime Jourdan }
163d4983983SMaxime Jourdan canvas->used[canvas_index] = 0;
164d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags);
165d4983983SMaxime Jourdan
166d4983983SMaxime Jourdan return 0;
167d4983983SMaxime Jourdan }
168d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_free);
169d4983983SMaxime Jourdan
meson_canvas_probe(struct platform_device * pdev)170d4983983SMaxime Jourdan static int meson_canvas_probe(struct platform_device *pdev)
171d4983983SMaxime Jourdan {
172d4983983SMaxime Jourdan struct meson_canvas *canvas;
173d4983983SMaxime Jourdan struct device *dev = &pdev->dev;
174d4983983SMaxime Jourdan
175d4983983SMaxime Jourdan canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL);
176d4983983SMaxime Jourdan if (!canvas)
177d4983983SMaxime Jourdan return -ENOMEM;
178d4983983SMaxime Jourdan
17997a4a240SCai Huoqing canvas->reg_base = devm_platform_ioremap_resource(pdev, 0);
180d4983983SMaxime Jourdan if (IS_ERR(canvas->reg_base))
181d4983983SMaxime Jourdan return PTR_ERR(canvas->reg_base);
182d4983983SMaxime Jourdan
1839a98fdf5SMartin Blumenstingl canvas->supports_endianness = of_device_get_match_data(dev);
1849a98fdf5SMartin Blumenstingl
185d4983983SMaxime Jourdan canvas->dev = dev;
186d4983983SMaxime Jourdan spin_lock_init(&canvas->lock);
187d4983983SMaxime Jourdan dev_set_drvdata(dev, canvas);
188d4983983SMaxime Jourdan
189d4983983SMaxime Jourdan return 0;
190d4983983SMaxime Jourdan }
191d4983983SMaxime Jourdan
192d4983983SMaxime Jourdan static const struct of_device_id canvas_dt_match[] = {
1939a98fdf5SMartin Blumenstingl { .compatible = "amlogic,meson8-canvas", .data = (void *)false, },
1949a98fdf5SMartin Blumenstingl { .compatible = "amlogic,meson8b-canvas", .data = (void *)false, },
1959a98fdf5SMartin Blumenstingl { .compatible = "amlogic,meson8m2-canvas", .data = (void *)false, },
1969a98fdf5SMartin Blumenstingl { .compatible = "amlogic,canvas", .data = (void *)true, },
197d4983983SMaxime Jourdan {}
198d4983983SMaxime Jourdan };
199d4983983SMaxime Jourdan MODULE_DEVICE_TABLE(of, canvas_dt_match);
200d4983983SMaxime Jourdan
201d4983983SMaxime Jourdan static struct platform_driver meson_canvas_driver = {
202d4983983SMaxime Jourdan .probe = meson_canvas_probe,
203d4983983SMaxime Jourdan .driver = {
204d4983983SMaxime Jourdan .name = "amlogic-canvas",
205d4983983SMaxime Jourdan .of_match_table = canvas_dt_match,
206d4983983SMaxime Jourdan },
207d4983983SMaxime Jourdan };
208d4983983SMaxime Jourdan module_platform_driver(meson_canvas_driver);
209d4983983SMaxime Jourdan
210d4983983SMaxime Jourdan MODULE_DESCRIPTION("Amlogic Canvas driver");
211d4983983SMaxime Jourdan MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
212d4983983SMaxime Jourdan MODULE_LICENSE("GPL");
213