1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2018 BayLibre, SAS
4 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
5 * Copyright (C) 2014 Endless Mobile
8 #include <linux/kernel.h>
9 #include <linux/mfd/syscon.h>
10 #include <linux/module.h>
11 #include <linux/regmap.h>
12 #include <linux/soc/amlogic/meson-canvas.h>
13 #include <linux/of_address.h>
14 #include <linux/of_platform.h>
17 #define NUM_CANVAS 256
20 #define DMC_CAV_LUT_DATAL 0x00
21 #define CANVAS_WIDTH_LBIT 29
22 #define CANVAS_WIDTH_LWID 3
23 #define DMC_CAV_LUT_DATAH 0x04
24 #define CANVAS_WIDTH_HBIT 0
25 #define CANVAS_HEIGHT_BIT 9
26 #define CANVAS_WRAP_BIT 22
27 #define CANVAS_BLKMODE_BIT 24
28 #define CANVAS_ENDIAN_BIT 26
29 #define DMC_CAV_LUT_ADDR 0x08
30 #define CANVAS_LUT_WR_EN BIT(9)
31 #define CANVAS_LUT_RD_EN BIT(8)
35 void __iomem
*reg_base
;
36 spinlock_t lock
; /* canvas device lock */
38 bool supports_endianness
;
41 static void canvas_write(struct meson_canvas
*canvas
, u32 reg
, u32 val
)
43 writel_relaxed(val
, canvas
->reg_base
+ reg
);
46 static u32
canvas_read(struct meson_canvas
*canvas
, u32 reg
)
48 return readl_relaxed(canvas
->reg_base
+ reg
);
51 struct meson_canvas
*meson_canvas_get(struct device
*dev
)
53 struct device_node
*canvas_node
;
54 struct platform_device
*canvas_pdev
;
55 struct meson_canvas
*canvas
;
57 canvas_node
= of_parse_phandle(dev
->of_node
, "amlogic,canvas", 0);
59 return ERR_PTR(-ENODEV
);
61 canvas_pdev
= of_find_device_by_node(canvas_node
);
63 of_node_put(canvas_node
);
64 return ERR_PTR(-EPROBE_DEFER
);
67 of_node_put(canvas_node
);
70 * If priv is NULL, it's probably because the canvas hasn't
71 * properly initialized. Bail out with -EINVAL because, in the
72 * current state, this driver probe cannot return -EPROBE_DEFER
74 canvas
= dev_get_drvdata(&canvas_pdev
->dev
);
76 put_device(&canvas_pdev
->dev
);
77 return ERR_PTR(-EINVAL
);
82 EXPORT_SYMBOL_GPL(meson_canvas_get
);
84 int meson_canvas_config(struct meson_canvas
*canvas
, u8 canvas_index
,
85 u32 addr
, u32 stride
, u32 height
,
92 if (endian
&& !canvas
->supports_endianness
) {
94 "Endianness is not supported on this SoC\n");
98 spin_lock_irqsave(&canvas
->lock
, flags
);
99 if (!canvas
->used
[canvas_index
]) {
101 "Trying to setup non allocated canvas %u\n",
103 spin_unlock_irqrestore(&canvas
->lock
, flags
);
107 canvas_write(canvas
, DMC_CAV_LUT_DATAL
,
109 (((stride
+ 7) >> 3) << CANVAS_WIDTH_LBIT
));
111 canvas_write(canvas
, DMC_CAV_LUT_DATAH
,
112 ((((stride
+ 7) >> 3) >> CANVAS_WIDTH_LWID
) <<
114 (height
<< CANVAS_HEIGHT_BIT
) |
115 (wrap
<< CANVAS_WRAP_BIT
) |
116 (blkmode
<< CANVAS_BLKMODE_BIT
) |
117 (endian
<< CANVAS_ENDIAN_BIT
));
119 canvas_write(canvas
, DMC_CAV_LUT_ADDR
,
120 CANVAS_LUT_WR_EN
| canvas_index
);
122 /* Force a read-back to make sure everything is flushed. */
123 canvas_read(canvas
, DMC_CAV_LUT_DATAH
);
124 spin_unlock_irqrestore(&canvas
->lock
, flags
);
128 EXPORT_SYMBOL_GPL(meson_canvas_config
);
130 int meson_canvas_alloc(struct meson_canvas
*canvas
, u8
*canvas_index
)
135 spin_lock_irqsave(&canvas
->lock
, flags
);
136 for (i
= 0; i
< NUM_CANVAS
; ++i
) {
137 if (!canvas
->used
[i
]) {
139 spin_unlock_irqrestore(&canvas
->lock
, flags
);
144 spin_unlock_irqrestore(&canvas
->lock
, flags
);
146 dev_err(canvas
->dev
, "No more canvas available\n");
149 EXPORT_SYMBOL_GPL(meson_canvas_alloc
);
151 int meson_canvas_free(struct meson_canvas
*canvas
, u8 canvas_index
)
155 spin_lock_irqsave(&canvas
->lock
, flags
);
156 if (!canvas
->used
[canvas_index
]) {
158 "Trying to free unused canvas %u\n", canvas_index
);
159 spin_unlock_irqrestore(&canvas
->lock
, flags
);
162 canvas
->used
[canvas_index
] = 0;
163 spin_unlock_irqrestore(&canvas
->lock
, flags
);
167 EXPORT_SYMBOL_GPL(meson_canvas_free
);
169 static int meson_canvas_probe(struct platform_device
*pdev
)
171 struct resource
*res
;
172 struct meson_canvas
*canvas
;
173 struct device
*dev
= &pdev
->dev
;
175 canvas
= devm_kzalloc(dev
, sizeof(*canvas
), GFP_KERNEL
);
179 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
180 canvas
->reg_base
= devm_ioremap_resource(dev
, res
);
181 if (IS_ERR(canvas
->reg_base
))
182 return PTR_ERR(canvas
->reg_base
);
184 canvas
->supports_endianness
= of_device_get_match_data(dev
);
187 spin_lock_init(&canvas
->lock
);
188 dev_set_drvdata(dev
, canvas
);
193 static const struct of_device_id canvas_dt_match
[] = {
194 { .compatible
= "amlogic,meson8-canvas", .data
= (void *)false, },
195 { .compatible
= "amlogic,meson8b-canvas", .data
= (void *)false, },
196 { .compatible
= "amlogic,meson8m2-canvas", .data
= (void *)false, },
197 { .compatible
= "amlogic,canvas", .data
= (void *)true, },
200 MODULE_DEVICE_TABLE(of
, canvas_dt_match
);
202 static struct platform_driver meson_canvas_driver
= {
203 .probe
= meson_canvas_probe
,
205 .name
= "amlogic-canvas",
206 .of_match_table
= canvas_dt_match
,
209 module_platform_driver(meson_canvas_driver
);
211 MODULE_DESCRIPTION("Amlogic Canvas driver");
212 MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
213 MODULE_LICENSE("GPL");