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