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 return ERR_PTR(-EINVAL
);
80 EXPORT_SYMBOL_GPL(meson_canvas_get
);
82 int meson_canvas_config(struct meson_canvas
*canvas
, u8 canvas_index
,
83 u32 addr
, u32 stride
, u32 height
,
90 if (endian
&& !canvas
->supports_endianness
) {
92 "Endianness is not supported on this SoC\n");
96 spin_lock_irqsave(&canvas
->lock
, flags
);
97 if (!canvas
->used
[canvas_index
]) {
99 "Trying to setup non allocated canvas %u\n",
101 spin_unlock_irqrestore(&canvas
->lock
, flags
);
105 canvas_write(canvas
, DMC_CAV_LUT_DATAL
,
107 (((stride
+ 7) >> 3) << CANVAS_WIDTH_LBIT
));
109 canvas_write(canvas
, DMC_CAV_LUT_DATAH
,
110 ((((stride
+ 7) >> 3) >> CANVAS_WIDTH_LWID
) <<
112 (height
<< CANVAS_HEIGHT_BIT
) |
113 (wrap
<< CANVAS_WRAP_BIT
) |
114 (blkmode
<< CANVAS_BLKMODE_BIT
) |
115 (endian
<< CANVAS_ENDIAN_BIT
));
117 canvas_write(canvas
, DMC_CAV_LUT_ADDR
,
118 CANVAS_LUT_WR_EN
| canvas_index
);
120 /* Force a read-back to make sure everything is flushed. */
121 canvas_read(canvas
, DMC_CAV_LUT_DATAH
);
122 spin_unlock_irqrestore(&canvas
->lock
, flags
);
126 EXPORT_SYMBOL_GPL(meson_canvas_config
);
128 int meson_canvas_alloc(struct meson_canvas
*canvas
, u8
*canvas_index
)
133 spin_lock_irqsave(&canvas
->lock
, flags
);
134 for (i
= 0; i
< NUM_CANVAS
; ++i
) {
135 if (!canvas
->used
[i
]) {
137 spin_unlock_irqrestore(&canvas
->lock
, flags
);
142 spin_unlock_irqrestore(&canvas
->lock
, flags
);
144 dev_err(canvas
->dev
, "No more canvas available\n");
147 EXPORT_SYMBOL_GPL(meson_canvas_alloc
);
149 int meson_canvas_free(struct meson_canvas
*canvas
, u8 canvas_index
)
153 spin_lock_irqsave(&canvas
->lock
, flags
);
154 if (!canvas
->used
[canvas_index
]) {
156 "Trying to free unused canvas %u\n", canvas_index
);
157 spin_unlock_irqrestore(&canvas
->lock
, flags
);
160 canvas
->used
[canvas_index
] = 0;
161 spin_unlock_irqrestore(&canvas
->lock
, flags
);
165 EXPORT_SYMBOL_GPL(meson_canvas_free
);
167 static int meson_canvas_probe(struct platform_device
*pdev
)
169 struct resource
*res
;
170 struct meson_canvas
*canvas
;
171 struct device
*dev
= &pdev
->dev
;
173 canvas
= devm_kzalloc(dev
, sizeof(*canvas
), GFP_KERNEL
);
177 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
178 canvas
->reg_base
= devm_ioremap_resource(dev
, res
);
179 if (IS_ERR(canvas
->reg_base
))
180 return PTR_ERR(canvas
->reg_base
);
182 canvas
->supports_endianness
= of_device_get_match_data(dev
);
185 spin_lock_init(&canvas
->lock
);
186 dev_set_drvdata(dev
, canvas
);
191 static const struct of_device_id canvas_dt_match
[] = {
192 { .compatible
= "amlogic,meson8-canvas", .data
= (void *)false, },
193 { .compatible
= "amlogic,meson8b-canvas", .data
= (void *)false, },
194 { .compatible
= "amlogic,meson8m2-canvas", .data
= (void *)false, },
195 { .compatible
= "amlogic,canvas", .data
= (void *)true, },
198 MODULE_DEVICE_TABLE(of
, canvas_dt_match
);
200 static struct platform_driver meson_canvas_driver
= {
201 .probe
= meson_canvas_probe
,
203 .name
= "amlogic-canvas",
204 .of_match_table
= canvas_dt_match
,
207 module_platform_driver(meson_canvas_driver
);
209 MODULE_DESCRIPTION("Amlogic Canvas driver");
210 MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
211 MODULE_LICENSE("GPL");