xref: /linux/drivers/soc/amlogic/meson-canvas.c (revision 28f851e6afa858f182802e23ac60c3ed7d1c04a1)
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>
11d4983983SMaxime Jourdan #include <linux/regmap.h>
12d4983983SMaxime Jourdan #include <linux/soc/amlogic/meson-canvas.h>
13d4983983SMaxime Jourdan #include <linux/of_address.h>
14d4983983SMaxime Jourdan #include <linux/of_platform.h>
15d4983983SMaxime Jourdan #include <linux/io.h>
16d4983983SMaxime Jourdan 
17d4983983SMaxime Jourdan #define NUM_CANVAS 256
18d4983983SMaxime Jourdan 
19d4983983SMaxime Jourdan /* DMC Registers */
20d4983983SMaxime Jourdan #define DMC_CAV_LUT_DATAL	0x00
21d4983983SMaxime Jourdan 	#define CANVAS_WIDTH_LBIT	29
22d4983983SMaxime Jourdan 	#define CANVAS_WIDTH_LWID	3
23d4983983SMaxime Jourdan #define DMC_CAV_LUT_DATAH	0x04
24d4983983SMaxime Jourdan 	#define CANVAS_WIDTH_HBIT	0
25d4983983SMaxime Jourdan 	#define CANVAS_HEIGHT_BIT	9
26d4983983SMaxime Jourdan 	#define CANVAS_WRAP_BIT		22
27d4983983SMaxime Jourdan 	#define CANVAS_BLKMODE_BIT	24
28d4983983SMaxime Jourdan 	#define CANVAS_ENDIAN_BIT	26
29d4983983SMaxime Jourdan #define DMC_CAV_LUT_ADDR	0x08
30d4983983SMaxime Jourdan 	#define CANVAS_LUT_WR_EN	BIT(9)
31d4983983SMaxime Jourdan 	#define CANVAS_LUT_RD_EN	BIT(8)
32d4983983SMaxime Jourdan 
33d4983983SMaxime Jourdan struct meson_canvas {
34d4983983SMaxime Jourdan 	struct device *dev;
35d4983983SMaxime Jourdan 	void __iomem *reg_base;
36d4983983SMaxime Jourdan 	spinlock_t lock; /* canvas device lock */
37d4983983SMaxime Jourdan 	u8 used[NUM_CANVAS];
389a98fdf5SMartin Blumenstingl 	bool supports_endianness;
39d4983983SMaxime Jourdan };
40d4983983SMaxime Jourdan 
41d4983983SMaxime Jourdan static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val)
42d4983983SMaxime Jourdan {
43d4983983SMaxime Jourdan 	writel_relaxed(val, canvas->reg_base + reg);
44d4983983SMaxime Jourdan }
45d4983983SMaxime Jourdan 
46d4983983SMaxime Jourdan static u32 canvas_read(struct meson_canvas *canvas, u32 reg)
47d4983983SMaxime Jourdan {
48d4983983SMaxime Jourdan 	return readl_relaxed(canvas->reg_base + reg);
49d4983983SMaxime Jourdan }
50d4983983SMaxime Jourdan 
51d4983983SMaxime Jourdan struct meson_canvas *meson_canvas_get(struct device *dev)
52d4983983SMaxime Jourdan {
53d4983983SMaxime Jourdan 	struct device_node *canvas_node;
54d4983983SMaxime Jourdan 	struct platform_device *canvas_pdev;
55382f8be0SNeil Armstrong 	struct meson_canvas *canvas;
56d4983983SMaxime Jourdan 
57d4983983SMaxime Jourdan 	canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
58d4983983SMaxime Jourdan 	if (!canvas_node)
59d4983983SMaxime Jourdan 		return ERR_PTR(-ENODEV);
60d4983983SMaxime Jourdan 
61d4983983SMaxime Jourdan 	canvas_pdev = of_find_device_by_node(canvas_node);
6299e5a8dfSwen yang 	if (!canvas_pdev) {
6399e5a8dfSwen yang 		of_node_put(canvas_node);
64d4983983SMaxime Jourdan 		return ERR_PTR(-EPROBE_DEFER);
6599e5a8dfSwen yang 	}
66d4983983SMaxime Jourdan 
6799e5a8dfSwen yang 	of_node_put(canvas_node);
68382f8be0SNeil Armstrong 
69382f8be0SNeil Armstrong 	/*
70382f8be0SNeil Armstrong 	 * If priv is NULL, it's probably because the canvas hasn't
71382f8be0SNeil Armstrong 	 * properly initialized. Bail out with -EINVAL because, in the
72382f8be0SNeil Armstrong 	 * current state, this driver probe cannot return -EPROBE_DEFER
73382f8be0SNeil Armstrong 	 */
74382f8be0SNeil Armstrong 	canvas = dev_get_drvdata(&canvas_pdev->dev);
75*28f851e6SYu Kuai 	if (!canvas) {
76*28f851e6SYu Kuai 		put_device(&canvas_pdev->dev);
77382f8be0SNeil Armstrong 		return ERR_PTR(-EINVAL);
78*28f851e6SYu Kuai 	}
79382f8be0SNeil Armstrong 
80382f8be0SNeil Armstrong 	return canvas;
81d4983983SMaxime Jourdan }
82d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_get);
83d4983983SMaxime Jourdan 
84d4983983SMaxime Jourdan int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
85d4983983SMaxime Jourdan 			u32 addr, u32 stride, u32 height,
86d4983983SMaxime Jourdan 			unsigned int wrap,
87d4983983SMaxime Jourdan 			unsigned int blkmode,
88d4983983SMaxime Jourdan 			unsigned int endian)
89d4983983SMaxime Jourdan {
90d4983983SMaxime Jourdan 	unsigned long flags;
91d4983983SMaxime Jourdan 
929a98fdf5SMartin Blumenstingl 	if (endian && !canvas->supports_endianness) {
939a98fdf5SMartin Blumenstingl 		dev_err(canvas->dev,
949a98fdf5SMartin Blumenstingl 			"Endianness is not supported on this SoC\n");
959a98fdf5SMartin Blumenstingl 		return -EINVAL;
969a98fdf5SMartin Blumenstingl 	}
979a98fdf5SMartin Blumenstingl 
98d4983983SMaxime Jourdan 	spin_lock_irqsave(&canvas->lock, flags);
99d4983983SMaxime Jourdan 	if (!canvas->used[canvas_index]) {
100d4983983SMaxime Jourdan 		dev_err(canvas->dev,
101d4983983SMaxime Jourdan 			"Trying to setup non allocated canvas %u\n",
102d4983983SMaxime Jourdan 			canvas_index);
103d4983983SMaxime Jourdan 		spin_unlock_irqrestore(&canvas->lock, flags);
104d4983983SMaxime Jourdan 		return -EINVAL;
105d4983983SMaxime Jourdan 	}
106d4983983SMaxime Jourdan 
107d4983983SMaxime Jourdan 	canvas_write(canvas, DMC_CAV_LUT_DATAL,
108d4983983SMaxime Jourdan 		     ((addr + 7) >> 3) |
109d4983983SMaxime Jourdan 		     (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
110d4983983SMaxime Jourdan 
111d4983983SMaxime Jourdan 	canvas_write(canvas, DMC_CAV_LUT_DATAH,
112d4983983SMaxime Jourdan 		     ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
113d4983983SMaxime Jourdan 						CANVAS_WIDTH_HBIT) |
114d4983983SMaxime Jourdan 		     (height << CANVAS_HEIGHT_BIT) |
115d4983983SMaxime Jourdan 		     (wrap << CANVAS_WRAP_BIT) |
116d4983983SMaxime Jourdan 		     (blkmode << CANVAS_BLKMODE_BIT) |
117d4983983SMaxime Jourdan 		     (endian << CANVAS_ENDIAN_BIT));
118d4983983SMaxime Jourdan 
119d4983983SMaxime Jourdan 	canvas_write(canvas, DMC_CAV_LUT_ADDR,
120d4983983SMaxime Jourdan 		     CANVAS_LUT_WR_EN | canvas_index);
121d4983983SMaxime Jourdan 
122d4983983SMaxime Jourdan 	/* Force a read-back to make sure everything is flushed. */
123d4983983SMaxime Jourdan 	canvas_read(canvas, DMC_CAV_LUT_DATAH);
124d4983983SMaxime Jourdan 	spin_unlock_irqrestore(&canvas->lock, flags);
125d4983983SMaxime Jourdan 
126d4983983SMaxime Jourdan 	return 0;
127d4983983SMaxime Jourdan }
128d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_config);
129d4983983SMaxime Jourdan 
130d4983983SMaxime Jourdan int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index)
131d4983983SMaxime Jourdan {
132d4983983SMaxime Jourdan 	int i;
133d4983983SMaxime Jourdan 	unsigned long flags;
134d4983983SMaxime Jourdan 
135d4983983SMaxime Jourdan 	spin_lock_irqsave(&canvas->lock, flags);
136d4983983SMaxime Jourdan 	for (i = 0; i < NUM_CANVAS; ++i) {
137d4983983SMaxime Jourdan 		if (!canvas->used[i]) {
138d4983983SMaxime Jourdan 			canvas->used[i] = 1;
139d4983983SMaxime Jourdan 			spin_unlock_irqrestore(&canvas->lock, flags);
140d4983983SMaxime Jourdan 			*canvas_index = i;
141d4983983SMaxime Jourdan 			return 0;
142d4983983SMaxime Jourdan 		}
143d4983983SMaxime Jourdan 	}
144d4983983SMaxime Jourdan 	spin_unlock_irqrestore(&canvas->lock, flags);
145d4983983SMaxime Jourdan 
146d4983983SMaxime Jourdan 	dev_err(canvas->dev, "No more canvas available\n");
147d4983983SMaxime Jourdan 	return -ENODEV;
148d4983983SMaxime Jourdan }
149d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_alloc);
150d4983983SMaxime Jourdan 
151d4983983SMaxime Jourdan int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index)
152d4983983SMaxime Jourdan {
153d4983983SMaxime Jourdan 	unsigned long flags;
154d4983983SMaxime Jourdan 
155d4983983SMaxime Jourdan 	spin_lock_irqsave(&canvas->lock, flags);
156d4983983SMaxime Jourdan 	if (!canvas->used[canvas_index]) {
157d4983983SMaxime Jourdan 		dev_err(canvas->dev,
158d4983983SMaxime Jourdan 			"Trying to free unused canvas %u\n", canvas_index);
159d4983983SMaxime Jourdan 		spin_unlock_irqrestore(&canvas->lock, flags);
160d4983983SMaxime Jourdan 		return -EINVAL;
161d4983983SMaxime Jourdan 	}
162d4983983SMaxime Jourdan 	canvas->used[canvas_index] = 0;
163d4983983SMaxime Jourdan 	spin_unlock_irqrestore(&canvas->lock, flags);
164d4983983SMaxime Jourdan 
165d4983983SMaxime Jourdan 	return 0;
166d4983983SMaxime Jourdan }
167d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_free);
168d4983983SMaxime Jourdan 
169d4983983SMaxime Jourdan static int meson_canvas_probe(struct platform_device *pdev)
170d4983983SMaxime Jourdan {
171d4983983SMaxime Jourdan 	struct resource *res;
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 
179d4983983SMaxime Jourdan 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
180d4983983SMaxime Jourdan 	canvas->reg_base = devm_ioremap_resource(dev, res);
181d4983983SMaxime Jourdan 	if (IS_ERR(canvas->reg_base))
182d4983983SMaxime Jourdan 		return PTR_ERR(canvas->reg_base);
183d4983983SMaxime Jourdan 
1849a98fdf5SMartin Blumenstingl 	canvas->supports_endianness = of_device_get_match_data(dev);
1859a98fdf5SMartin Blumenstingl 
186d4983983SMaxime Jourdan 	canvas->dev = dev;
187d4983983SMaxime Jourdan 	spin_lock_init(&canvas->lock);
188d4983983SMaxime Jourdan 	dev_set_drvdata(dev, canvas);
189d4983983SMaxime Jourdan 
190d4983983SMaxime Jourdan 	return 0;
191d4983983SMaxime Jourdan }
192d4983983SMaxime Jourdan 
193d4983983SMaxime Jourdan static const struct of_device_id canvas_dt_match[] = {
1949a98fdf5SMartin Blumenstingl 	{ .compatible = "amlogic,meson8-canvas", .data = (void *)false, },
1959a98fdf5SMartin Blumenstingl 	{ .compatible = "amlogic,meson8b-canvas", .data = (void *)false, },
1969a98fdf5SMartin Blumenstingl 	{ .compatible = "amlogic,meson8m2-canvas", .data = (void *)false, },
1979a98fdf5SMartin Blumenstingl 	{ .compatible = "amlogic,canvas", .data = (void *)true, },
198d4983983SMaxime Jourdan 	{}
199d4983983SMaxime Jourdan };
200d4983983SMaxime Jourdan MODULE_DEVICE_TABLE(of, canvas_dt_match);
201d4983983SMaxime Jourdan 
202d4983983SMaxime Jourdan static struct platform_driver meson_canvas_driver = {
203d4983983SMaxime Jourdan 	.probe = meson_canvas_probe,
204d4983983SMaxime Jourdan 	.driver = {
205d4983983SMaxime Jourdan 		.name = "amlogic-canvas",
206d4983983SMaxime Jourdan 		.of_match_table = canvas_dt_match,
207d4983983SMaxime Jourdan 	},
208d4983983SMaxime Jourdan };
209d4983983SMaxime Jourdan module_platform_driver(meson_canvas_driver);
210d4983983SMaxime Jourdan 
211d4983983SMaxime Jourdan MODULE_DESCRIPTION("Amlogic Canvas driver");
212d4983983SMaxime Jourdan MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
213d4983983SMaxime Jourdan MODULE_LICENSE("GPL");
214