aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBoris Brezillon <boris.brezillon@free-electrons.com>2015-01-06 05:13:28 -0500
committerBoris Brezillon <boris.brezillon@free-electrons.com>2015-01-21 03:46:02 -0500
commit1a396789f65a22711eecddc5c13c0dec6bd753c7 (patch)
tree93f1e9c3e08cbf41f5c744da30fef918e712a03c
parentbb276cb3a3844f6b60e5a35b23e89ae41fc64c48 (diff)
drm: add Atmel HLCDC Display Controller support
The Atmel HLCDC (HLCD Controller) IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family) provides a display controller device. This display controller supports at least one primary plane and might provide several overlays and an hardware cursor depending on the IP version. At the moment, this driver only implements an RGB connector to interface with LCD panels, but support for other kind of external devices might be added later. Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> Reviewed-by: Rob Clark <robdclark@gmail.com> Tested-by: Anthony Harivel <anthony.harivel@emtrion.de> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com> Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/Kconfig11
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/Makefile7
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c406
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c579
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h213
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c667
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h398
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c319
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c856
11 files changed, 3459 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index c3413b6adb17..ea283894a12a 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -183,6 +183,8 @@ source "drivers/gpu/drm/cirrus/Kconfig"
183 183
184source "drivers/gpu/drm/armada/Kconfig" 184source "drivers/gpu/drm/armada/Kconfig"
185 185
186source "drivers/gpu/drm/atmel-hlcdc/Kconfig"
187
186source "drivers/gpu/drm/rcar-du/Kconfig" 188source "drivers/gpu/drm/rcar-du/Kconfig"
187 189
188source "drivers/gpu/drm/shmobile/Kconfig" 190source "drivers/gpu/drm/shmobile/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 66e40398b3d3..9d92ba3ff10c 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_DRM_GMA500) += gma500/
54obj-$(CONFIG_DRM_UDL) += udl/ 54obj-$(CONFIG_DRM_UDL) += udl/
55obj-$(CONFIG_DRM_AST) += ast/ 55obj-$(CONFIG_DRM_AST) += ast/
56obj-$(CONFIG_DRM_ARMADA) += armada/ 56obj-$(CONFIG_DRM_ARMADA) += armada/
57obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/
57obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ 58obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
58obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ 59obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
59obj-$(CONFIG_DRM_OMAP) += omapdrm/ 60obj-$(CONFIG_DRM_OMAP) += omapdrm/
diff --git a/drivers/gpu/drm/atmel-hlcdc/Kconfig b/drivers/gpu/drm/atmel-hlcdc/Kconfig
new file mode 100644
index 000000000000..1a085625538a
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/Kconfig
@@ -0,0 +1,11 @@
1config DRM_ATMEL_HLCDC
2 tristate "DRM Support for ATMEL HLCDC Display Controller"
3 depends on DRM && OF && COMMON_CLK && MFD_ATMEL_HLCDC
4 select DRM_GEM_CMA_HELPER
5 select DRM_KMS_HELPER
6 select DRM_KMS_FB_HELPER
7 select DRM_KMS_CMA_HELPER
8 select DRM_PANEL
9 help
10 Choose this option if you have an ATMEL SoC with an HLCDC display
11 controller (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family).
diff --git a/drivers/gpu/drm/atmel-hlcdc/Makefile b/drivers/gpu/drm/atmel-hlcdc/Makefile
new file mode 100644
index 000000000000..10ae426e60bd
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/Makefile
@@ -0,0 +1,7 @@
1atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \
2 atmel_hlcdc_dc.o \
3 atmel_hlcdc_layer.o \
4 atmel_hlcdc_output.o \
5 atmel_hlcdc_plane.o
6
7obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc-dc.o
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
new file mode 100644
index 000000000000..0409b907de5d
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -0,0 +1,406 @@
1/*
2 * Copyright (C) 2014 Traphandler
3 * Copyright (C) 2014 Free Electrons
4 *
5 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
6 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published by
10 * the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <linux/clk.h>
22#include <linux/pm.h>
23#include <linux/pm_runtime.h>
24
25#include <drm/drm_crtc.h>
26#include <drm/drm_crtc_helper.h>
27#include <drm/drmP.h>
28
29#include <video/videomode.h>
30
31#include "atmel_hlcdc_dc.h"
32
33/**
34 * Atmel HLCDC CRTC structure
35 *
36 * @base: base DRM CRTC structure
37 * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
38 * @event: pointer to the current page flip event
39 * @id: CRTC id (returned by drm_crtc_index)
40 * @dpms: DPMS mode
41 */
42struct atmel_hlcdc_crtc {
43 struct drm_crtc base;
44 struct atmel_hlcdc_dc *dc;
45 struct drm_pending_vblank_event *event;
46 int id;
47 int dpms;
48};
49
50static inline struct atmel_hlcdc_crtc *
51drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
52{
53 return container_of(crtc, struct atmel_hlcdc_crtc, base);
54}
55
56static void atmel_hlcdc_crtc_dpms(struct drm_crtc *c, int mode)
57{
58 struct drm_device *dev = c->dev;
59 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
60 struct regmap *regmap = crtc->dc->hlcdc->regmap;
61 unsigned int status;
62
63 if (mode != DRM_MODE_DPMS_ON)
64 mode = DRM_MODE_DPMS_OFF;
65
66 if (crtc->dpms == mode)
67 return;
68
69 pm_runtime_get_sync(dev->dev);
70
71 if (mode != DRM_MODE_DPMS_ON) {
72 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
73 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
74 (status & ATMEL_HLCDC_DISP))
75 cpu_relax();
76
77 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
78 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
79 (status & ATMEL_HLCDC_SYNC))
80 cpu_relax();
81
82 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
83 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
84 (status & ATMEL_HLCDC_PIXEL_CLK))
85 cpu_relax();
86
87 clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
88
89 pm_runtime_allow(dev->dev);
90 } else {
91 pm_runtime_forbid(dev->dev);
92
93 clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
94
95 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
96 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
97 !(status & ATMEL_HLCDC_PIXEL_CLK))
98 cpu_relax();
99
100
101 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
102 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
103 !(status & ATMEL_HLCDC_SYNC))
104 cpu_relax();
105
106 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
107 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
108 !(status & ATMEL_HLCDC_DISP))
109 cpu_relax();
110 }
111
112 pm_runtime_put_sync(dev->dev);
113
114 crtc->dpms = mode;
115}
116
117static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c,
118 struct drm_display_mode *mode,
119 struct drm_display_mode *adj,
120 int x, int y,
121 struct drm_framebuffer *old_fb)
122{
123 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
124 struct regmap *regmap = crtc->dc->hlcdc->regmap;
125 struct drm_plane *plane = c->primary;
126 struct drm_framebuffer *fb;
127 unsigned long mode_rate;
128 struct videomode vm;
129 unsigned long prate;
130 unsigned int cfg;
131 int div;
132
133 if (atmel_hlcdc_dc_mode_valid(crtc->dc, adj) != MODE_OK)
134 return -EINVAL;
135
136 vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay;
137 vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end;
138 vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start;
139 vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay;
140 vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end;
141 vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start;
142
143 regmap_write(regmap, ATMEL_HLCDC_CFG(1),
144 (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
145
146 regmap_write(regmap, ATMEL_HLCDC_CFG(2),
147 (vm.vfront_porch - 1) | (vm.vback_porch << 16));
148
149 regmap_write(regmap, ATMEL_HLCDC_CFG(3),
150 (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
151
152 regmap_write(regmap, ATMEL_HLCDC_CFG(4),
153 (adj->crtc_hdisplay - 1) |
154 ((adj->crtc_vdisplay - 1) << 16));
155
156 cfg = ATMEL_HLCDC_CLKPOL;
157
158 prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
159 mode_rate = mode->crtc_clock * 1000;
160 if ((prate / 2) < mode_rate) {
161 prate *= 2;
162 cfg |= ATMEL_HLCDC_CLKSEL;
163 }
164
165 div = DIV_ROUND_UP(prate, mode_rate);
166 if (div < 2)
167 div = 2;
168
169 cfg |= ATMEL_HLCDC_CLKDIV(div);
170
171 regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0),
172 ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK |
173 ATMEL_HLCDC_CLKPOL, cfg);
174
175 cfg = 0;
176
177 if (mode->flags & DRM_MODE_FLAG_NVSYNC)
178 cfg |= ATMEL_HLCDC_VSPOL;
179
180 if (mode->flags & DRM_MODE_FLAG_NHSYNC)
181 cfg |= ATMEL_HLCDC_HSPOL;
182
183 regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5),
184 ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
185 ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
186 ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
187 ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
188 ATMEL_HLCDC_GUARDTIME_MASK,
189 cfg);
190
191 fb = plane->fb;
192 plane->fb = old_fb;
193
194 return atmel_hlcdc_plane_update_with_mode(plane, c, fb, 0, 0,
195 adj->hdisplay, adj->vdisplay,
196 x << 16, y << 16,
197 adj->hdisplay << 16,
198 adj->vdisplay << 16,
199 adj);
200}
201
202int atmel_hlcdc_crtc_mode_set_base(struct drm_crtc *c, int x, int y,
203 struct drm_framebuffer *old_fb)
204{
205 struct drm_plane *plane = c->primary;
206 struct drm_framebuffer *fb = plane->fb;
207 struct drm_display_mode *mode = &c->hwmode;
208
209 plane->fb = old_fb;
210
211 return plane->funcs->update_plane(plane, c, fb,
212 0, 0,
213 mode->hdisplay,
214 mode->vdisplay,
215 x << 16, y << 16,
216 mode->hdisplay << 16,
217 mode->vdisplay << 16);
218}
219
220static void atmel_hlcdc_crtc_prepare(struct drm_crtc *crtc)
221{
222 atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
223}
224
225static void atmel_hlcdc_crtc_commit(struct drm_crtc *crtc)
226{
227 atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
228}
229
230static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc,
231 const struct drm_display_mode *mode,
232 struct drm_display_mode *adjusted_mode)
233{
234 return true;
235}
236
237static void atmel_hlcdc_crtc_disable(struct drm_crtc *crtc)
238{
239 struct drm_plane *plane;
240
241 atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
242 crtc->primary->funcs->disable_plane(crtc->primary);
243
244 drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
245 if (plane->crtc != crtc)
246 continue;
247
248 plane->funcs->disable_plane(crtc->primary);
249 plane->crtc = NULL;
250 }
251}
252
253static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
254 .mode_fixup = atmel_hlcdc_crtc_mode_fixup,
255 .dpms = atmel_hlcdc_crtc_dpms,
256 .mode_set = atmel_hlcdc_crtc_mode_set,
257 .mode_set_base = atmel_hlcdc_crtc_mode_set_base,
258 .prepare = atmel_hlcdc_crtc_prepare,
259 .commit = atmel_hlcdc_crtc_commit,
260 .disable = atmel_hlcdc_crtc_disable,
261};
262
263static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c)
264{
265 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
266
267 drm_crtc_cleanup(c);
268 kfree(crtc);
269}
270
271void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *c,
272 struct drm_file *file)
273{
274 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
275 struct drm_pending_vblank_event *event;
276 struct drm_device *dev = c->dev;
277 unsigned long flags;
278
279 spin_lock_irqsave(&dev->event_lock, flags);
280 event = crtc->event;
281 if (event && event->base.file_priv == file) {
282 event->base.destroy(&event->base);
283 drm_vblank_put(dev, crtc->id);
284 crtc->event = NULL;
285 }
286 spin_unlock_irqrestore(&dev->event_lock, flags);
287}
288
289static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc)
290{
291 struct drm_device *dev = crtc->base.dev;
292 unsigned long flags;
293
294 spin_lock_irqsave(&dev->event_lock, flags);
295 if (crtc->event) {
296 drm_send_vblank_event(dev, crtc->id, crtc->event);
297 drm_vblank_put(dev, crtc->id);
298 crtc->event = NULL;
299 }
300 spin_unlock_irqrestore(&dev->event_lock, flags);
301}
302
303void atmel_hlcdc_crtc_irq(struct drm_crtc *c)
304{
305 drm_handle_vblank(c->dev, 0);
306 atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c));
307}
308
309static int atmel_hlcdc_crtc_page_flip(struct drm_crtc *c,
310 struct drm_framebuffer *fb,
311 struct drm_pending_vblank_event *event,
312 uint32_t page_flip_flags)
313{
314 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
315 struct atmel_hlcdc_plane_update_req req;
316 struct drm_plane *plane = c->primary;
317 struct drm_device *dev = c->dev;
318 unsigned long flags;
319 int ret = 0;
320
321 spin_lock_irqsave(&dev->event_lock, flags);
322 if (crtc->event)
323 ret = -EBUSY;
324 spin_unlock_irqrestore(&dev->event_lock, flags);
325
326 if (ret)
327 return ret;
328
329 memset(&req, 0, sizeof(req));
330 req.crtc_x = 0;
331 req.crtc_y = 0;
332 req.crtc_h = c->mode.crtc_vdisplay;
333 req.crtc_w = c->mode.crtc_hdisplay;
334 req.src_x = c->x << 16;
335 req.src_y = c->y << 16;
336 req.src_w = req.crtc_w << 16;
337 req.src_h = req.crtc_h << 16;
338 req.fb = fb;
339
340 ret = atmel_hlcdc_plane_prepare_update_req(plane, &req, &c->hwmode);
341 if (ret)
342 return ret;
343
344 if (event) {
345 drm_vblank_get(c->dev, crtc->id);
346 spin_lock_irqsave(&dev->event_lock, flags);
347 crtc->event = event;
348 spin_unlock_irqrestore(&dev->event_lock, flags);
349 }
350
351 ret = atmel_hlcdc_plane_apply_update_req(plane, &req);
352 if (ret)
353 crtc->event = NULL;
354 else
355 plane->fb = fb;
356
357 return ret;
358}
359
360static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
361 .page_flip = atmel_hlcdc_crtc_page_flip,
362 .set_config = drm_crtc_helper_set_config,
363 .destroy = atmel_hlcdc_crtc_destroy,
364};
365
366int atmel_hlcdc_crtc_create(struct drm_device *dev)
367{
368 struct atmel_hlcdc_dc *dc = dev->dev_private;
369 struct atmel_hlcdc_planes *planes = dc->planes;
370 struct atmel_hlcdc_crtc *crtc;
371 int ret;
372 int i;
373
374 crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
375 if (!crtc)
376 return -ENOMEM;
377
378 crtc->dpms = DRM_MODE_DPMS_OFF;
379 crtc->dc = dc;
380
381 ret = drm_crtc_init_with_planes(dev, &crtc->base,
382 &planes->primary->base,
383 planes->cursor ? &planes->cursor->base : NULL,
384 &atmel_hlcdc_crtc_funcs);
385 if (ret < 0)
386 goto fail;
387
388 crtc->id = drm_crtc_index(&crtc->base);
389
390 if (planes->cursor)
391 planes->cursor->base.possible_crtcs = 1 << crtc->id;
392
393 for (i = 0; i < planes->noverlays; i++)
394 planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
395
396 drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
397
398 dc->crtc = &crtc->base;
399
400 return 0;
401
402fail:
403 atmel_hlcdc_crtc_destroy(&crtc->base);
404 return ret;
405}
406
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
new file mode 100644
index 000000000000..7320a6c6613f
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
@@ -0,0 +1,579 @@
1/*
2 * Copyright (C) 2014 Traphandler
3 * Copyright (C) 2014 Free Electrons
4 * Copyright (C) 2014 Atmel
5 *
6 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
7 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published by
11 * the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <linux/clk.h>
23#include <linux/irq.h>
24#include <linux/irqchip.h>
25#include <linux/module.h>
26#include <linux/pm_runtime.h>
27
28#include "atmel_hlcdc_dc.h"
29
30#define ATMEL_HLCDC_LAYER_IRQS_OFFSET 8
31
32static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
33 {
34 .name = "base",
35 .formats = &atmel_hlcdc_plane_rgb_formats,
36 .regs_offset = 0x40,
37 .id = 0,
38 .type = ATMEL_HLCDC_BASE_LAYER,
39 .nconfigs = 7,
40 .layout = {
41 .xstride = { 2 },
42 .default_color = 3,
43 .general_config = 4,
44 .disc_pos = 5,
45 .disc_size = 6,
46 },
47 },
48 {
49 .name = "overlay1",
50 .formats = &atmel_hlcdc_plane_rgb_formats,
51 .regs_offset = 0x140,
52 .id = 1,
53 .type = ATMEL_HLCDC_OVERLAY_LAYER,
54 .nconfigs = 10,
55 .layout = {
56 .pos = 2,
57 .size = 3,
58 .xstride = { 4 },
59 .pstride = { 5 },
60 .default_color = 6,
61 .chroma_key = 7,
62 .chroma_key_mask = 8,
63 .general_config = 9,
64 },
65 },
66 {
67 .name = "overlay2",
68 .formats = &atmel_hlcdc_plane_rgb_formats,
69 .regs_offset = 0x240,
70 .id = 2,
71 .type = ATMEL_HLCDC_OVERLAY_LAYER,
72 .nconfigs = 10,
73 .layout = {
74 .pos = 2,
75 .size = 3,
76 .xstride = { 4 },
77 .pstride = { 5 },
78 .default_color = 6,
79 .chroma_key = 7,
80 .chroma_key_mask = 8,
81 .general_config = 9,
82 },
83 },
84 {
85 .name = "high-end-overlay",
86 .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
87 .regs_offset = 0x340,
88 .id = 3,
89 .type = ATMEL_HLCDC_OVERLAY_LAYER,
90 .nconfigs = 42,
91 .layout = {
92 .pos = 2,
93 .size = 3,
94 .memsize = 4,
95 .xstride = { 5, 7 },
96 .pstride = { 6, 8 },
97 .default_color = 9,
98 .chroma_key = 10,
99 .chroma_key_mask = 11,
100 .general_config = 12,
101 .csc = 14,
102 },
103 },
104 {
105 .name = "cursor",
106 .formats = &atmel_hlcdc_plane_rgb_formats,
107 .regs_offset = 0x440,
108 .id = 4,
109 .type = ATMEL_HLCDC_CURSOR_LAYER,
110 .nconfigs = 10,
111 .max_width = 128,
112 .max_height = 128,
113 .layout = {
114 .pos = 2,
115 .size = 3,
116 .xstride = { 4 },
117 .pstride = { 5 },
118 .default_color = 6,
119 .chroma_key = 7,
120 .chroma_key_mask = 8,
121 .general_config = 9,
122 },
123 },
124};
125
126static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = {
127 .min_width = 0,
128 .min_height = 0,
129 .max_width = 2048,
130 .max_height = 2048,
131 .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers),
132 .layers = atmel_hlcdc_sama5d3_layers,
133};
134
135static const struct of_device_id atmel_hlcdc_of_match[] = {
136 {
137 .compatible = "atmel,sama5d3-hlcdc",
138 .data = &atmel_hlcdc_dc_sama5d3,
139 },
140 { /* sentinel */ },
141};
142
143int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
144 struct drm_display_mode *mode)
145{
146 int vfront_porch = mode->vsync_start - mode->vdisplay;
147 int vback_porch = mode->vtotal - mode->vsync_end;
148 int vsync_len = mode->vsync_end - mode->vsync_start;
149 int hfront_porch = mode->hsync_start - mode->hdisplay;
150 int hback_porch = mode->htotal - mode->hsync_end;
151 int hsync_len = mode->hsync_end - mode->hsync_start;
152
153 if (hsync_len > 0x40 || hsync_len < 1)
154 return MODE_HSYNC;
155
156 if (vsync_len > 0x40 || vsync_len < 1)
157 return MODE_VSYNC;
158
159 if (hfront_porch > 0x200 || hfront_porch < 1 ||
160 hback_porch > 0x200 || hback_porch < 1 ||
161 mode->hdisplay < 1)
162 return MODE_H_ILLEGAL;
163
164 if (vfront_porch > 0x40 || vfront_porch < 1 ||
165 vback_porch > 0x40 || vback_porch < 0 ||
166 mode->vdisplay < 1)
167 return MODE_V_ILLEGAL;
168
169 return MODE_OK;
170}
171
172static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
173{
174 struct drm_device *dev = data;
175 struct atmel_hlcdc_dc *dc = dev->dev_private;
176 unsigned long status;
177 unsigned int imr, isr;
178 int i;
179
180 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr);
181 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
182 status = imr & isr;
183 if (!status)
184 return IRQ_NONE;
185
186 if (status & ATMEL_HLCDC_SOF)
187 atmel_hlcdc_crtc_irq(dc->crtc);
188
189 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
190 struct atmel_hlcdc_layer *layer = dc->layers[i];
191
192 if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer)
193 continue;
194
195 atmel_hlcdc_layer_irq(layer);
196 }
197
198 return IRQ_HANDLED;
199}
200
201static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev,
202 struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
203{
204 return drm_fb_cma_create(dev, file_priv, mode_cmd);
205}
206
207static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
208{
209 struct atmel_hlcdc_dc *dc = dev->dev_private;
210
211 if (dc->fbdev) {
212 drm_fbdev_cma_hotplug_event(dc->fbdev);
213 } else {
214 dc->fbdev = drm_fbdev_cma_init(dev, 24,
215 dev->mode_config.num_crtc,
216 dev->mode_config.num_connector);
217 if (IS_ERR(dc->fbdev))
218 dc->fbdev = NULL;
219 }
220}
221
222static const struct drm_mode_config_funcs mode_config_funcs = {
223 .fb_create = atmel_hlcdc_fb_create,
224 .output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
225};
226
227static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
228{
229 struct atmel_hlcdc_dc *dc = dev->dev_private;
230 struct atmel_hlcdc_planes *planes;
231 int ret;
232 int i;
233
234 drm_mode_config_init(dev);
235
236 ret = atmel_hlcdc_create_outputs(dev);
237 if (ret) {
238 dev_err(dev->dev, "failed to create panel: %d\n", ret);
239 return ret;
240 }
241
242 planes = atmel_hlcdc_create_planes(dev);
243 if (IS_ERR(planes)) {
244 dev_err(dev->dev, "failed to create planes\n");
245 return PTR_ERR(planes);
246 }
247
248 dc->planes = planes;
249
250 dc->layers[planes->primary->layer.desc->id] =
251 &planes->primary->layer;
252
253 if (planes->cursor)
254 dc->layers[planes->cursor->layer.desc->id] =
255 &planes->cursor->layer;
256
257 for (i = 0; i < planes->noverlays; i++)
258 dc->layers[planes->overlays[i]->layer.desc->id] =
259 &planes->overlays[i]->layer;
260
261 ret = atmel_hlcdc_crtc_create(dev);
262 if (ret) {
263 dev_err(dev->dev, "failed to create crtc\n");
264 return ret;
265 }
266
267 dev->mode_config.min_width = dc->desc->min_width;
268 dev->mode_config.min_height = dc->desc->min_height;
269 dev->mode_config.max_width = dc->desc->max_width;
270 dev->mode_config.max_height = dc->desc->max_height;
271 dev->mode_config.funcs = &mode_config_funcs;
272
273 return 0;
274}
275
276static int atmel_hlcdc_dc_load(struct drm_device *dev)
277{
278 struct platform_device *pdev = to_platform_device(dev->dev);
279 const struct of_device_id *match;
280 struct atmel_hlcdc_dc *dc;
281 int ret;
282
283 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node);
284 if (!match) {
285 dev_err(&pdev->dev, "invalid compatible string\n");
286 return -ENODEV;
287 }
288
289 if (!match->data) {
290 dev_err(&pdev->dev, "invalid hlcdc description\n");
291 return -EINVAL;
292 }
293
294 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL);
295 if (!dc)
296 return -ENOMEM;
297
298 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0);
299 if (!dc->wq)
300 return -ENOMEM;
301
302 dc->desc = match->data;
303 dc->hlcdc = dev_get_drvdata(dev->dev->parent);
304 dev->dev_private = dc;
305
306 ret = clk_prepare_enable(dc->hlcdc->periph_clk);
307 if (ret) {
308 dev_err(dev->dev, "failed to enable periph_clk\n");
309 goto err_destroy_wq;
310 }
311
312 pm_runtime_enable(dev->dev);
313
314 pm_runtime_put_sync(dev->dev);
315
316 ret = atmel_hlcdc_dc_modeset_init(dev);
317 if (ret < 0) {
318 dev_err(dev->dev, "failed to initialize mode setting\n");
319 goto err_periph_clk_disable;
320 }
321
322 ret = drm_vblank_init(dev, 1);
323 if (ret < 0) {
324 dev_err(dev->dev, "failed to initialize vblank\n");
325 goto err_periph_clk_disable;
326 }
327
328 pm_runtime_get_sync(dev->dev);
329 ret = drm_irq_install(dev, dc->hlcdc->irq);
330 pm_runtime_put_sync(dev->dev);
331 if (ret < 0) {
332 dev_err(dev->dev, "failed to install IRQ handler\n");
333 goto err_periph_clk_disable;
334 }
335
336 platform_set_drvdata(pdev, dev);
337
338 drm_kms_helper_poll_init(dev);
339
340 /* force connectors detection */
341 drm_helper_hpd_irq_event(dev);
342
343 return 0;
344
345err_periph_clk_disable:
346 pm_runtime_disable(dev->dev);
347 clk_disable_unprepare(dc->hlcdc->periph_clk);
348
349err_destroy_wq:
350 destroy_workqueue(dc->wq);
351
352 return ret;
353}
354
355static void atmel_hlcdc_dc_unload(struct drm_device *dev)
356{
357 struct atmel_hlcdc_dc *dc = dev->dev_private;
358
359 if (dc->fbdev)
360 drm_fbdev_cma_fini(dc->fbdev);
361 flush_workqueue(dc->wq);
362 drm_kms_helper_poll_fini(dev);
363 drm_mode_config_cleanup(dev);
364 drm_vblank_cleanup(dev);
365
366 pm_runtime_get_sync(dev->dev);
367 drm_irq_uninstall(dev);
368 pm_runtime_put_sync(dev->dev);
369
370 dev->dev_private = NULL;
371
372 pm_runtime_disable(dev->dev);
373 clk_disable_unprepare(dc->hlcdc->periph_clk);
374 destroy_workqueue(dc->wq);
375}
376
377static int atmel_hlcdc_dc_connector_plug_all(struct drm_device *dev)
378{
379 struct drm_connector *connector, *failed;
380 int ret;
381
382 mutex_lock(&dev->mode_config.mutex);
383 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
384 ret = drm_connector_register(connector);
385 if (ret) {
386 failed = connector;
387 goto err;
388 }
389 }
390 mutex_unlock(&dev->mode_config.mutex);
391 return 0;
392
393err:
394 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
395 if (failed == connector)
396 break;
397
398 drm_connector_unregister(connector);
399 }
400 mutex_unlock(&dev->mode_config.mutex);
401
402 return ret;
403}
404
405static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev)
406{
407 mutex_lock(&dev->mode_config.mutex);
408 drm_connector_unplug_all(dev);
409 mutex_unlock(&dev->mode_config.mutex);
410}
411
412static void atmel_hlcdc_dc_preclose(struct drm_device *dev,
413 struct drm_file *file)
414{
415 struct drm_crtc *crtc;
416
417 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
418 atmel_hlcdc_crtc_cancel_page_flip(crtc, file);
419}
420
421static void atmel_hlcdc_dc_lastclose(struct drm_device *dev)
422{
423 struct atmel_hlcdc_dc *dc = dev->dev_private;
424
425 drm_fbdev_cma_restore_mode(dc->fbdev);
426}
427
428static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev)
429{
430 struct atmel_hlcdc_dc *dc = dev->dev_private;
431 unsigned int cfg = 0;
432 int i;
433
434 /* Enable interrupts on activated layers */
435 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
436 if (dc->layers[i])
437 cfg |= ATMEL_HLCDC_LAYER_STATUS(i);
438 }
439
440 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg);
441
442 return 0;
443}
444
445static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev)
446{
447 struct atmel_hlcdc_dc *dc = dev->dev_private;
448 unsigned int isr;
449
450 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff);
451 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
452}
453
454static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, int crtc)
455{
456 struct atmel_hlcdc_dc *dc = dev->dev_private;
457
458 /* Enable SOF (Start Of Frame) interrupt for vblank counting */
459 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF);
460
461 return 0;
462}
463
464static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, int crtc)
465{
466 struct atmel_hlcdc_dc *dc = dev->dev_private;
467
468 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF);
469}
470
471static const struct file_operations fops = {
472 .owner = THIS_MODULE,
473 .open = drm_open,
474 .release = drm_release,
475 .unlocked_ioctl = drm_ioctl,
476#ifdef CONFIG_COMPAT
477 .compat_ioctl = drm_compat_ioctl,
478#endif
479 .poll = drm_poll,
480 .read = drm_read,
481 .llseek = no_llseek,
482 .mmap = drm_gem_cma_mmap,
483};
484
485static struct drm_driver atmel_hlcdc_dc_driver = {
486 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
487 .preclose = atmel_hlcdc_dc_preclose,
488 .lastclose = atmel_hlcdc_dc_lastclose,
489 .irq_handler = atmel_hlcdc_dc_irq_handler,
490 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall,
491 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall,
492 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall,
493 .get_vblank_counter = drm_vblank_count,
494 .enable_vblank = atmel_hlcdc_dc_enable_vblank,
495 .disable_vblank = atmel_hlcdc_dc_disable_vblank,
496 .gem_free_object = drm_gem_cma_free_object,
497 .gem_vm_ops = &drm_gem_cma_vm_ops,
498 .dumb_create = drm_gem_cma_dumb_create,
499 .dumb_map_offset = drm_gem_cma_dumb_map_offset,
500 .dumb_destroy = drm_gem_dumb_destroy,
501 .fops = &fops,
502 .name = "atmel-hlcdc",
503 .desc = "Atmel HLCD Controller DRM",
504 .date = "20141504",
505 .major = 1,
506 .minor = 0,
507};
508
509static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
510{
511 struct drm_device *ddev;
512 int ret;
513
514 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev);
515 if (!ddev)
516 return -ENOMEM;
517
518 ret = drm_dev_set_unique(ddev, dev_name(ddev->dev));
519 if (ret)
520 goto err_unref;
521
522 ret = atmel_hlcdc_dc_load(ddev);
523 if (ret)
524 goto err_unref;
525
526 ret = drm_dev_register(ddev, 0);
527 if (ret)
528 goto err_unload;
529
530 ret = atmel_hlcdc_dc_connector_plug_all(ddev);
531 if (ret)
532 goto err_unregister;
533
534 return 0;
535
536err_unregister:
537 drm_dev_unregister(ddev);
538
539err_unload:
540 atmel_hlcdc_dc_unload(ddev);
541
542err_unref:
543 drm_dev_unref(ddev);
544
545 return ret;
546}
547
548static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
549{
550 struct drm_device *ddev = platform_get_drvdata(pdev);
551
552 atmel_hlcdc_dc_connector_unplug_all(ddev);
553 drm_dev_unregister(ddev);
554 atmel_hlcdc_dc_unload(ddev);
555 drm_dev_unref(ddev);
556
557 return 0;
558}
559
560static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
561 { .compatible = "atmel,hlcdc-display-controller" },
562 { },
563};
564
565static struct platform_driver atmel_hlcdc_dc_platform_driver = {
566 .probe = atmel_hlcdc_dc_drm_probe,
567 .remove = atmel_hlcdc_dc_drm_remove,
568 .driver = {
569 .name = "atmel-hlcdc-display-controller",
570 .of_match_table = atmel_hlcdc_dc_of_match,
571 },
572};
573module_platform_driver(atmel_hlcdc_dc_platform_driver);
574
575MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
576MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
577MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver");
578MODULE_LICENSE("GPL");
579MODULE_ALIAS("platform:atmel-hlcdc-dc");
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
new file mode 100644
index 000000000000..7bc96af3397a
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
@@ -0,0 +1,213 @@
1/*
2 * Copyright (C) 2014 Traphandler
3 * Copyright (C) 2014 Free Electrons
4 * Copyright (C) 2014 Atmel
5 *
6 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
7 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published by
11 * the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#ifndef DRM_ATMEL_HLCDC_H
23#define DRM_ATMEL_HLCDC_H
24
25#include <linux/clk.h>
26#include <linux/irqdomain.h>
27#include <linux/pwm.h>
28
29#include <drm/drm_crtc.h>
30#include <drm/drm_crtc_helper.h>
31#include <drm/drm_fb_cma_helper.h>
32#include <drm/drm_gem_cma_helper.h>
33#include <drm/drm_panel.h>
34#include <drm/drmP.h>
35
36#include "atmel_hlcdc_layer.h"
37
38#define ATMEL_HLCDC_MAX_LAYERS 5
39
40/**
41 * Atmel HLCDC Display Controller description structure.
42 *
43 * This structure describe the HLCDC IP capabilities and depends on the
44 * HLCDC IP version (or Atmel SoC family).
45 *
46 * @min_width: minimum width supported by the Display Controller
47 * @min_height: minimum height supported by the Display Controller
48 * @max_width: maximum width supported by the Display Controller
49 * @max_height: maximum height supported by the Display Controller
50 * @layers: a layer description table describing available layers
51 * @nlayers: layer description table size
52 */
53struct atmel_hlcdc_dc_desc {
54 int min_width;
55 int min_height;
56 int max_width;
57 int max_height;
58 const struct atmel_hlcdc_layer_desc *layers;
59 int nlayers;
60};
61
62/**
63 * Atmel HLCDC Plane properties.
64 *
65 * This structure stores plane property definitions.
66 *
67 * @alpha: alpha blending (or transparency) property
68 * @rotation: rotation property
69 */
70struct atmel_hlcdc_plane_properties {
71 struct drm_property *alpha;
72 struct drm_property *rotation;
73};
74
75/**
76 * Atmel HLCDC Plane.
77 *
78 * @base: base DRM plane structure
79 * @layer: HLCDC layer structure
80 * @properties: pointer to the property definitions structure
81 * @rotation: current rotation status
82 */
83struct atmel_hlcdc_plane {
84 struct drm_plane base;
85 struct atmel_hlcdc_layer layer;
86 struct atmel_hlcdc_plane_properties *properties;
87 unsigned int rotation;
88};
89
90static inline struct atmel_hlcdc_plane *
91drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
92{
93 return container_of(p, struct atmel_hlcdc_plane, base);
94}
95
96static inline struct atmel_hlcdc_plane *
97atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l)
98{
99 return container_of(l, struct atmel_hlcdc_plane, layer);
100}
101
102/**
103 * Atmel HLCDC Plane update request structure.
104 *
105 * @crtc_x: x position of the plane relative to the CRTC
106 * @crtc_y: y position of the plane relative to the CRTC
107 * @crtc_w: visible width of the plane
108 * @crtc_h: visible height of the plane
109 * @src_x: x buffer position
110 * @src_y: y buffer position
111 * @src_w: buffer width
112 * @src_h: buffer height
113 * @fb: framebuffer object object
114 * @bpp: bytes per pixel deduced from pixel_format
115 * @offsets: offsets to apply to the GEM buffers
116 * @xstride: value to add to the pixel pointer between each line
117 * @pstride: value to add to the pixel pointer between each pixel
118 * @nplanes: number of planes (deduced from pixel_format)
119 */
120struct atmel_hlcdc_plane_update_req {
121 int crtc_x;
122 int crtc_y;
123 unsigned int crtc_w;
124 unsigned int crtc_h;
125 uint32_t src_x;
126 uint32_t src_y;
127 uint32_t src_w;
128 uint32_t src_h;
129 struct drm_framebuffer *fb;
130
131 /* These fields are private and should not be touched */
132 int bpp[ATMEL_HLCDC_MAX_PLANES];
133 unsigned int offsets[ATMEL_HLCDC_MAX_PLANES];
134 int xstride[ATMEL_HLCDC_MAX_PLANES];
135 int pstride[ATMEL_HLCDC_MAX_PLANES];
136 int nplanes;
137};
138
139/**
140 * Atmel HLCDC Planes.
141 *
142 * This structure stores the instantiated HLCDC Planes and can be accessed by
143 * the HLCDC Display Controller or the HLCDC CRTC.
144 *
145 * @primary: primary plane
146 * @cursor: hardware cursor plane
147 * @overlays: overlay plane table
148 * @noverlays: number of overlay planes
149 */
150struct atmel_hlcdc_planes {
151 struct atmel_hlcdc_plane *primary;
152 struct atmel_hlcdc_plane *cursor;
153 struct atmel_hlcdc_plane **overlays;
154 int noverlays;
155};
156
157/**
158 * Atmel HLCDC Display Controller.
159 *
160 * @desc: HLCDC Display Controller description
161 * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
162 * @fbdev: framebuffer device attached to the Display Controller
163 * @crtc: CRTC provided by the display controller
164 * @planes: instantiated planes
165 * @layers: active HLCDC layer
166 * @wq: display controller workqueue
167 */
168struct atmel_hlcdc_dc {
169 const struct atmel_hlcdc_dc_desc *desc;
170 struct atmel_hlcdc *hlcdc;
171 struct drm_fbdev_cma *fbdev;
172 struct drm_crtc *crtc;
173 struct atmel_hlcdc_planes *planes;
174 struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
175 struct workqueue_struct *wq;
176};
177
178extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;
179extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats;
180
181int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc,
182 struct drm_display_mode *mode);
183
184struct atmel_hlcdc_planes *
185atmel_hlcdc_create_planes(struct drm_device *dev);
186
187int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
188 struct atmel_hlcdc_plane_update_req *req,
189 const struct drm_display_mode *mode);
190
191int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
192 struct atmel_hlcdc_plane_update_req *req);
193
194int atmel_hlcdc_plane_update_with_mode(struct drm_plane *p,
195 struct drm_crtc *crtc,
196 struct drm_framebuffer *fb,
197 int crtc_x, int crtc_y,
198 unsigned int crtc_w,
199 unsigned int crtc_h,
200 uint32_t src_x, uint32_t src_y,
201 uint32_t src_w, uint32_t src_h,
202 const struct drm_display_mode *mode);
203
204void atmel_hlcdc_crtc_irq(struct drm_crtc *c);
205
206void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *crtc,
207 struct drm_file *file);
208
209int atmel_hlcdc_crtc_create(struct drm_device *dev);
210
211int atmel_hlcdc_create_outputs(struct drm_device *dev);
212
213#endif /* DRM_ATMEL_HLCDC_H */
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
new file mode 100644
index 000000000000..063d2a7b941f
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
@@ -0,0 +1,667 @@
1/*
2 * Copyright (C) 2014 Free Electrons
3 * Copyright (C) 2014 Atmel
4 *
5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/dma-mapping.h>
21#include <linux/interrupt.h>
22
23#include "atmel_hlcdc_dc.h"
24
25static void
26atmel_hlcdc_layer_fb_flip_release(struct drm_flip_work *work, void *val)
27{
28 struct atmel_hlcdc_layer_fb_flip *flip = val;
29
30 if (flip->fb)
31 drm_framebuffer_unreference(flip->fb);
32 kfree(flip);
33}
34
35static void
36atmel_hlcdc_layer_fb_flip_destroy(struct atmel_hlcdc_layer_fb_flip *flip)
37{
38 if (flip->fb)
39 drm_framebuffer_unreference(flip->fb);
40 kfree(flip->task);
41 kfree(flip);
42}
43
44static void
45atmel_hlcdc_layer_fb_flip_release_queue(struct atmel_hlcdc_layer *layer,
46 struct atmel_hlcdc_layer_fb_flip *flip)
47{
48 int i;
49
50 if (!flip)
51 return;
52
53 for (i = 0; i < layer->max_planes; i++) {
54 if (!flip->dscrs[i])
55 break;
56
57 flip->dscrs[i]->status = 0;
58 flip->dscrs[i] = NULL;
59 }
60
61 drm_flip_work_queue_task(&layer->gc, flip->task);
62 drm_flip_work_commit(&layer->gc, layer->wq);
63}
64
65static void atmel_hlcdc_layer_update_reset(struct atmel_hlcdc_layer *layer,
66 int id)
67{
68 struct atmel_hlcdc_layer_update *upd = &layer->update;
69 struct atmel_hlcdc_layer_update_slot *slot;
70
71 if (id < 0 || id > 1)
72 return;
73
74 slot = &upd->slots[id];
75 bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs);
76 memset(slot->configs, 0,
77 sizeof(*slot->configs) * layer->desc->nconfigs);
78
79 if (slot->fb_flip) {
80 atmel_hlcdc_layer_fb_flip_release_queue(layer, slot->fb_flip);
81 slot->fb_flip = NULL;
82 }
83}
84
85static void atmel_hlcdc_layer_update_apply(struct atmel_hlcdc_layer *layer)
86{
87 struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
88 const struct atmel_hlcdc_layer_desc *desc = layer->desc;
89 struct atmel_hlcdc_layer_update *upd = &layer->update;
90 struct regmap *regmap = layer->hlcdc->regmap;
91 struct atmel_hlcdc_layer_update_slot *slot;
92 struct atmel_hlcdc_layer_fb_flip *fb_flip;
93 struct atmel_hlcdc_dma_channel_dscr *dscr;
94 unsigned int cfg;
95 u32 action = 0;
96 int i = 0;
97
98 if (upd->pending < 0 || upd->pending > 1)
99 return;
100
101 slot = &upd->slots[upd->pending];
102
103 for_each_set_bit(cfg, slot->updated_configs, layer->desc->nconfigs) {
104 regmap_write(regmap,
105 desc->regs_offset +
106 ATMEL_HLCDC_LAYER_CFG(layer, cfg),
107 slot->configs[cfg]);
108 action |= ATMEL_HLCDC_LAYER_UPDATE;
109 }
110
111 fb_flip = slot->fb_flip;
112
113 if (!fb_flip->fb)
114 goto apply;
115
116 if (dma->status == ATMEL_HLCDC_LAYER_DISABLED) {
117 for (i = 0; i < fb_flip->ngems; i++) {
118 dscr = fb_flip->dscrs[i];
119 dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
120 ATMEL_HLCDC_LAYER_DMA_IRQ |
121 ATMEL_HLCDC_LAYER_ADD_IRQ |
122 ATMEL_HLCDC_LAYER_DONE_IRQ;
123
124 regmap_write(regmap,
125 desc->regs_offset +
126 ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
127 dscr->addr);
128 regmap_write(regmap,
129 desc->regs_offset +
130 ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
131 dscr->ctrl);
132 regmap_write(regmap,
133 desc->regs_offset +
134 ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
135 dscr->next);
136 }
137
138 action |= ATMEL_HLCDC_LAYER_DMA_CHAN;
139 dma->status = ATMEL_HLCDC_LAYER_ENABLED;
140 } else {
141 for (i = 0; i < fb_flip->ngems; i++) {
142 dscr = fb_flip->dscrs[i];
143 dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
144 ATMEL_HLCDC_LAYER_DMA_IRQ |
145 ATMEL_HLCDC_LAYER_DSCR_IRQ |
146 ATMEL_HLCDC_LAYER_DONE_IRQ;
147
148 regmap_write(regmap,
149 desc->regs_offset +
150 ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
151 dscr->next);
152 }
153
154 action |= ATMEL_HLCDC_LAYER_A2Q;
155 }
156
157 /* Release unneeded descriptors */
158 for (i = fb_flip->ngems; i < layer->max_planes; i++) {
159 fb_flip->dscrs[i]->status = 0;
160 fb_flip->dscrs[i] = NULL;
161 }
162
163 dma->queue = fb_flip;
164 slot->fb_flip = NULL;
165
166apply:
167 if (action)
168 regmap_write(regmap,
169 desc->regs_offset + ATMEL_HLCDC_LAYER_CHER,
170 action);
171
172 atmel_hlcdc_layer_update_reset(layer, upd->pending);
173
174 upd->pending = -1;
175}
176
177void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
178{
179 struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
180 const struct atmel_hlcdc_layer_desc *desc = layer->desc;
181 struct regmap *regmap = layer->hlcdc->regmap;
182 struct atmel_hlcdc_layer_fb_flip *flip;
183 unsigned long flags;
184 unsigned int isr, imr;
185 unsigned int status;
186 unsigned int plane_status;
187 u32 flip_status;
188
189 int i;
190
191 regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IMR, &imr);
192 regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
193 status = imr & isr;
194 if (!status)
195 return;
196
197 spin_lock_irqsave(&layer->lock, flags);
198
199 flip = dma->queue ? dma->queue : dma->cur;
200
201 if (!flip) {
202 spin_unlock_irqrestore(&layer->lock, flags);
203 return;
204 }
205
206 /*
207 * Set LOADED and DONE flags: they'll be cleared if at least one
208 * memory plane is not LOADED or DONE.
209 */
210 flip_status = ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED |
211 ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
212 for (i = 0; i < flip->ngems; i++) {
213 plane_status = (status >> (8 * i));
214
215 if (plane_status &
216 (ATMEL_HLCDC_LAYER_ADD_IRQ |
217 ATMEL_HLCDC_LAYER_DSCR_IRQ) &
218 ~flip->dscrs[i]->ctrl) {
219 flip->dscrs[i]->status |=
220 ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
221 flip->dscrs[i]->ctrl |=
222 ATMEL_HLCDC_LAYER_ADD_IRQ |
223 ATMEL_HLCDC_LAYER_DSCR_IRQ;
224 }
225
226 if (plane_status &
227 ATMEL_HLCDC_LAYER_DONE_IRQ &
228 ~flip->dscrs[i]->ctrl) {
229 flip->dscrs[i]->status |=
230 ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
231 flip->dscrs[i]->ctrl |=
232 ATMEL_HLCDC_LAYER_DONE_IRQ;
233 }
234
235 if (plane_status & ATMEL_HLCDC_LAYER_OVR_IRQ)
236 flip->dscrs[i]->status |=
237 ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN;
238
239 /*
240 * Clear LOADED and DONE flags if the memory plane is either
241 * not LOADED or not DONE.
242 */
243 if (!(flip->dscrs[i]->status &
244 ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED))
245 flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
246
247 if (!(flip->dscrs[i]->status &
248 ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE))
249 flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
250
251 /*
252 * An overrun on one memory plane impact the whole framebuffer
253 * transfer, hence we set the OVERRUN flag as soon as there's
254 * one memory plane reporting such an overrun.
255 */
256 flip_status |= flip->dscrs[i]->status &
257 ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN;
258 }
259
260 /* Get changed bits */
261 flip_status ^= flip->status;
262 flip->status |= flip_status;
263
264 if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED) {
265 atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
266 dma->cur = dma->queue;
267 dma->queue = NULL;
268 }
269
270 if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE) {
271 atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
272 dma->cur = NULL;
273 }
274
275 if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN) {
276 regmap_write(regmap,
277 desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
278 ATMEL_HLCDC_LAYER_RST);
279 if (dma->queue)
280 atmel_hlcdc_layer_fb_flip_release_queue(layer,
281 dma->queue);
282
283 if (dma->cur)
284 atmel_hlcdc_layer_fb_flip_release_queue(layer,
285 dma->cur);
286
287 dma->cur = NULL;
288 dma->queue = NULL;
289 }
290
291 if (!dma->queue) {
292 atmel_hlcdc_layer_update_apply(layer);
293
294 if (!dma->cur)
295 dma->status = ATMEL_HLCDC_LAYER_DISABLED;
296 }
297
298 spin_unlock_irqrestore(&layer->lock, flags);
299}
300
301int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
302{
303 struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
304 struct atmel_hlcdc_layer_update *upd = &layer->update;
305 struct regmap *regmap = layer->hlcdc->regmap;
306 const struct atmel_hlcdc_layer_desc *desc = layer->desc;
307 unsigned long flags;
308 unsigned int isr;
309
310 spin_lock_irqsave(&layer->lock, flags);
311
312 /* Disable the layer */
313 regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
314 ATMEL_HLCDC_LAYER_RST);
315
316 /* Clear all pending interrupts */
317 regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
318
319 /* Discard current and queued framebuffer transfers. */
320 if (dma->cur) {
321 atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
322 dma->cur = NULL;
323 }
324
325 if (dma->queue) {
326 atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->queue);
327 dma->queue = NULL;
328 }
329
330 /*
331 * Then discard the pending update request (if any) to prevent
332 * DMA irq handler from restarting the DMA channel after it has
333 * been disabled.
334 */
335 if (upd->pending >= 0) {
336 atmel_hlcdc_layer_update_reset(layer, upd->pending);
337 upd->pending = -1;
338 }
339
340 dma->status = ATMEL_HLCDC_LAYER_DISABLED;
341
342 spin_unlock_irqrestore(&layer->lock, flags);
343
344 return 0;
345}
346
347int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer)
348{
349 struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
350 struct atmel_hlcdc_layer_update *upd = &layer->update;
351 struct regmap *regmap = layer->hlcdc->regmap;
352 struct atmel_hlcdc_layer_fb_flip *fb_flip;
353 struct atmel_hlcdc_layer_update_slot *slot;
354 unsigned long flags;
355 int i, j = 0;
356
357 fb_flip = kzalloc(sizeof(*fb_flip), GFP_KERNEL);
358 if (!fb_flip)
359 return -ENOMEM;
360
361 fb_flip->task = drm_flip_work_allocate_task(fb_flip, GFP_KERNEL);
362 if (!fb_flip->task) {
363 kfree(fb_flip);
364 return -ENOMEM;
365 }
366
367 spin_lock_irqsave(&layer->lock, flags);
368
369 upd->next = upd->pending ? 0 : 1;
370
371 slot = &upd->slots[upd->next];
372
373 for (i = 0; i < layer->max_planes * 4; i++) {
374 if (!dma->dscrs[i].status) {
375 fb_flip->dscrs[j++] = &dma->dscrs[i];
376 dma->dscrs[i].status =
377 ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED;
378 if (j == layer->max_planes)
379 break;
380 }
381 }
382
383 if (j < layer->max_planes) {
384 for (i = 0; i < j; i++)
385 fb_flip->dscrs[i]->status = 0;
386 }
387
388 if (j < layer->max_planes) {
389 spin_unlock_irqrestore(&layer->lock, flags);
390 atmel_hlcdc_layer_fb_flip_destroy(fb_flip);
391 return -EBUSY;
392 }
393
394 slot->fb_flip = fb_flip;
395
396 if (upd->pending >= 0) {
397 memcpy(slot->configs,
398 upd->slots[upd->pending].configs,
399 layer->desc->nconfigs * sizeof(u32));
400 memcpy(slot->updated_configs,
401 upd->slots[upd->pending].updated_configs,
402 DIV_ROUND_UP(layer->desc->nconfigs,
403 BITS_PER_BYTE * sizeof(unsigned long)) *
404 sizeof(unsigned long));
405 slot->fb_flip->fb = upd->slots[upd->pending].fb_flip->fb;
406 if (upd->slots[upd->pending].fb_flip->fb) {
407 slot->fb_flip->fb =
408 upd->slots[upd->pending].fb_flip->fb;
409 slot->fb_flip->ngems =
410 upd->slots[upd->pending].fb_flip->ngems;
411 drm_framebuffer_reference(slot->fb_flip->fb);
412 }
413 } else {
414 regmap_bulk_read(regmap,
415 layer->desc->regs_offset +
416 ATMEL_HLCDC_LAYER_CFG(layer, 0),
417 upd->slots[upd->next].configs,
418 layer->desc->nconfigs);
419 }
420
421 spin_unlock_irqrestore(&layer->lock, flags);
422
423 return 0;
424}
425
426void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer)
427{
428 struct atmel_hlcdc_layer_update *upd = &layer->update;
429
430 atmel_hlcdc_layer_update_reset(layer, upd->next);
431 upd->next = -1;
432}
433
434void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
435 struct drm_framebuffer *fb,
436 unsigned int *offsets)
437{
438 struct atmel_hlcdc_layer_update *upd = &layer->update;
439 struct atmel_hlcdc_layer_fb_flip *fb_flip;
440 struct atmel_hlcdc_layer_update_slot *slot;
441 struct atmel_hlcdc_dma_channel_dscr *dscr;
442 struct drm_framebuffer *old_fb;
443 int nplanes = 0;
444 int i;
445
446 if (upd->next < 0 || upd->next > 1)
447 return;
448
449 if (fb)
450 nplanes = drm_format_num_planes(fb->pixel_format);
451
452 if (nplanes > layer->max_planes)
453 return;
454
455 slot = &upd->slots[upd->next];
456
457 fb_flip = slot->fb_flip;
458 old_fb = slot->fb_flip->fb;
459
460 for (i = 0; i < nplanes; i++) {
461 struct drm_gem_cma_object *gem;
462
463 dscr = slot->fb_flip->dscrs[i];
464 gem = drm_fb_cma_get_gem_obj(fb, i);
465 dscr->addr = gem->paddr + offsets[i];
466 }
467
468 fb_flip->ngems = nplanes;
469 fb_flip->fb = fb;
470
471 if (fb)
472 drm_framebuffer_reference(fb);
473
474 if (old_fb)
475 drm_framebuffer_unreference(old_fb);
476}
477
478void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
479 u32 mask, u32 val)
480{
481 struct atmel_hlcdc_layer_update *upd = &layer->update;
482 struct atmel_hlcdc_layer_update_slot *slot;
483
484 if (upd->next < 0 || upd->next > 1)
485 return;
486
487 if (cfg >= layer->desc->nconfigs)
488 return;
489
490 slot = &upd->slots[upd->next];
491 slot->configs[cfg] &= ~mask;
492 slot->configs[cfg] |= (val & mask);
493 set_bit(cfg, slot->updated_configs);
494}
495
496void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer)
497{
498 struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
499 struct atmel_hlcdc_layer_update *upd = &layer->update;
500 struct atmel_hlcdc_layer_update_slot *slot;
501 unsigned long flags;
502
503 if (upd->next < 0 || upd->next > 1)
504 return;
505
506 slot = &upd->slots[upd->next];
507
508 spin_lock_irqsave(&layer->lock, flags);
509
510 /*
511 * Release pending update request and replace it by the new one.
512 */
513 if (upd->pending >= 0)
514 atmel_hlcdc_layer_update_reset(layer, upd->pending);
515
516 upd->pending = upd->next;
517 upd->next = -1;
518
519 if (!dma->queue)
520 atmel_hlcdc_layer_update_apply(layer);
521
522 spin_unlock_irqrestore(&layer->lock, flags);
523
524
525 upd->next = -1;
526}
527
528static int atmel_hlcdc_layer_dma_init(struct drm_device *dev,
529 struct atmel_hlcdc_layer *layer)
530{
531 struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
532 dma_addr_t dma_addr;
533 int i;
534
535 dma->dscrs = dma_alloc_coherent(dev->dev,
536 layer->max_planes * 4 *
537 sizeof(*dma->dscrs),
538 &dma_addr, GFP_KERNEL);
539 if (!dma->dscrs)
540 return -ENOMEM;
541
542 for (i = 0; i < layer->max_planes * 4; i++) {
543 struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
544
545 dscr->next = dma_addr + (i * sizeof(*dscr));
546 }
547
548 return 0;
549}
550
551static void atmel_hlcdc_layer_dma_cleanup(struct drm_device *dev,
552 struct atmel_hlcdc_layer *layer)
553{
554 struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
555 int i;
556
557 for (i = 0; i < layer->max_planes * 4; i++) {
558 struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
559
560 dscr->status = 0;
561 }
562
563 dma_free_coherent(dev->dev, layer->max_planes * 4 *
564 sizeof(*dma->dscrs), dma->dscrs,
565 dma->dscrs[0].next);
566}
567
568static int atmel_hlcdc_layer_update_init(struct drm_device *dev,
569 struct atmel_hlcdc_layer *layer,
570 const struct atmel_hlcdc_layer_desc *desc)
571{
572 struct atmel_hlcdc_layer_update *upd = &layer->update;
573 int updated_size;
574 void *buffer;
575 int i;
576
577 updated_size = DIV_ROUND_UP(desc->nconfigs,
578 BITS_PER_BYTE *
579 sizeof(unsigned long));
580
581 buffer = devm_kzalloc(dev->dev,
582 ((desc->nconfigs * sizeof(u32)) +
583 (updated_size * sizeof(unsigned long))) * 2,
584 GFP_KERNEL);
585 if (!buffer)
586 return -ENOMEM;
587
588 for (i = 0; i < 2; i++) {
589 upd->slots[i].updated_configs = buffer;
590 buffer += updated_size * sizeof(unsigned long);
591 upd->slots[i].configs = buffer;
592 buffer += desc->nconfigs * sizeof(u32);
593 }
594
595 upd->pending = -1;
596 upd->next = -1;
597
598 return 0;
599}
600
601int atmel_hlcdc_layer_init(struct drm_device *dev,
602 struct atmel_hlcdc_layer *layer,
603 const struct atmel_hlcdc_layer_desc *desc)
604{
605 struct atmel_hlcdc_dc *dc = dev->dev_private;
606 struct regmap *regmap = dc->hlcdc->regmap;
607 unsigned int tmp;
608 int ret;
609 int i;
610
611 layer->hlcdc = dc->hlcdc;
612 layer->wq = dc->wq;
613 layer->desc = desc;
614
615 regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
616 ATMEL_HLCDC_LAYER_RST);
617 for (i = 0; i < desc->formats->nformats; i++) {
618 int nplanes = drm_format_num_planes(desc->formats->formats[i]);
619
620 if (nplanes > layer->max_planes)
621 layer->max_planes = nplanes;
622 }
623
624 spin_lock_init(&layer->lock);
625 drm_flip_work_init(&layer->gc, desc->name,
626 atmel_hlcdc_layer_fb_flip_release);
627 ret = atmel_hlcdc_layer_dma_init(dev, layer);
628 if (ret)
629 return ret;
630
631 ret = atmel_hlcdc_layer_update_init(dev, layer, desc);
632 if (ret)
633 return ret;
634
635 /* Flush Status Register */
636 regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
637 0xffffffff);
638 regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR,
639 &tmp);
640
641 tmp = 0;
642 for (i = 0; i < layer->max_planes; i++)
643 tmp |= (ATMEL_HLCDC_LAYER_DMA_IRQ |
644 ATMEL_HLCDC_LAYER_DSCR_IRQ |
645 ATMEL_HLCDC_LAYER_ADD_IRQ |
646 ATMEL_HLCDC_LAYER_DONE_IRQ |
647 ATMEL_HLCDC_LAYER_OVR_IRQ) << (8 * i);
648
649 regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IER, tmp);
650
651 return 0;
652}
653
654void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
655 struct atmel_hlcdc_layer *layer)
656{
657 const struct atmel_hlcdc_layer_desc *desc = layer->desc;
658 struct regmap *regmap = layer->hlcdc->regmap;
659
660 regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
661 0xffffffff);
662 regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
663 ATMEL_HLCDC_LAYER_RST);
664
665 atmel_hlcdc_layer_dma_cleanup(dev, layer);
666 drm_flip_work_cleanup(&layer->gc);
667}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
new file mode 100644
index 000000000000..27e56c0862ec
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
@@ -0,0 +1,398 @@
1/*
2 * Copyright (C) 2014 Free Electrons
3 * Copyright (C) 2014 Atmel
4 *
5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef DRM_ATMEL_HLCDC_LAYER_H
21#define DRM_ATMEL_HLCDC_LAYER_H
22
23#include <linux/mfd/atmel-hlcdc.h>
24
25#include <drm/drm_crtc.h>
26#include <drm/drm_flip_work.h>
27#include <drm/drmP.h>
28
29#define ATMEL_HLCDC_LAYER_CHER 0x0
30#define ATMEL_HLCDC_LAYER_CHDR 0x4
31#define ATMEL_HLCDC_LAYER_CHSR 0x8
32#define ATMEL_HLCDC_LAYER_DMA_CHAN BIT(0)
33#define ATMEL_HLCDC_LAYER_UPDATE BIT(1)
34#define ATMEL_HLCDC_LAYER_A2Q BIT(2)
35#define ATMEL_HLCDC_LAYER_RST BIT(8)
36
37#define ATMEL_HLCDC_LAYER_IER 0xc
38#define ATMEL_HLCDC_LAYER_IDR 0x10
39#define ATMEL_HLCDC_LAYER_IMR 0x14
40#define ATMEL_HLCDC_LAYER_ISR 0x18
41#define ATMEL_HLCDC_LAYER_DFETCH BIT(0)
42#define ATMEL_HLCDC_LAYER_LFETCH BIT(1)
43#define ATMEL_HLCDC_LAYER_DMA_IRQ BIT(2)
44#define ATMEL_HLCDC_LAYER_DSCR_IRQ BIT(3)
45#define ATMEL_HLCDC_LAYER_ADD_IRQ BIT(4)
46#define ATMEL_HLCDC_LAYER_DONE_IRQ BIT(5)
47#define ATMEL_HLCDC_LAYER_OVR_IRQ BIT(6)
48
49#define ATMEL_HLCDC_LAYER_PLANE_HEAD(n) (((n) * 0x10) + 0x1c)
50#define ATMEL_HLCDC_LAYER_PLANE_ADDR(n) (((n) * 0x10) + 0x20)
51#define ATMEL_HLCDC_LAYER_PLANE_CTRL(n) (((n) * 0x10) + 0x24)
52#define ATMEL_HLCDC_LAYER_PLANE_NEXT(n) (((n) * 0x10) + 0x28)
53#define ATMEL_HLCDC_LAYER_CFG(p, c) (((c) * 4) + ((p)->max_planes * 0x10) + 0x1c)
54
55#define ATMEL_HLCDC_LAYER_DMA_CFG_ID 0
56#define ATMEL_HLCDC_LAYER_DMA_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_DMA_CFG_ID)
57#define ATMEL_HLCDC_LAYER_DMA_SIF BIT(0)
58#define ATMEL_HLCDC_LAYER_DMA_BLEN_MASK GENMASK(5, 4)
59#define ATMEL_HLCDC_LAYER_DMA_BLEN_SINGLE (0 << 4)
60#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR4 (1 << 4)
61#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR8 (2 << 4)
62#define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 (3 << 4)
63#define ATMEL_HLCDC_LAYER_DMA_DLBO BIT(8)
64#define ATMEL_HLCDC_LAYER_DMA_ROTDIS BIT(12)
65#define ATMEL_HLCDC_LAYER_DMA_LOCKDIS BIT(13)
66
67#define ATMEL_HLCDC_LAYER_FORMAT_CFG_ID 1
68#define ATMEL_HLCDC_LAYER_FORMAT_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_FORMAT_CFG_ID)
69#define ATMEL_HLCDC_LAYER_RGB (0 << 0)
70#define ATMEL_HLCDC_LAYER_CLUT (1 << 0)
71#define ATMEL_HLCDC_LAYER_YUV (2 << 0)
72#define ATMEL_HLCDC_RGB_MODE(m) (((m) & 0xf) << 4)
73#define ATMEL_HLCDC_CLUT_MODE(m) (((m) & 0x3) << 8)
74#define ATMEL_HLCDC_YUV_MODE(m) (((m) & 0xf) << 12)
75#define ATMEL_HLCDC_YUV422ROT BIT(16)
76#define ATMEL_HLCDC_YUV422SWP BIT(17)
77#define ATMEL_HLCDC_DSCALEOPT BIT(20)
78
79#define ATMEL_HLCDC_XRGB4444_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(0))
80#define ATMEL_HLCDC_ARGB4444_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(1))
81#define ATMEL_HLCDC_RGBA4444_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(2))
82#define ATMEL_HLCDC_RGB565_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(3))
83#define ATMEL_HLCDC_ARGB1555_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(4))
84#define ATMEL_HLCDC_XRGB8888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(9))
85#define ATMEL_HLCDC_RGB888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(10))
86#define ATMEL_HLCDC_ARGB8888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(12))
87#define ATMEL_HLCDC_RGBA8888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(13))
88
89#define ATMEL_HLCDC_AYUV_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(0))
90#define ATMEL_HLCDC_YUYV_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(1))
91#define ATMEL_HLCDC_UYVY_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(2))
92#define ATMEL_HLCDC_YVYU_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(3))
93#define ATMEL_HLCDC_VYUY_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(4))
94#define ATMEL_HLCDC_NV61_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(5))
95#define ATMEL_HLCDC_YUV422_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(6))
96#define ATMEL_HLCDC_NV21_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(7))
97#define ATMEL_HLCDC_YUV420_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(8))
98
99#define ATMEL_HLCDC_LAYER_POS_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pos)
100#define ATMEL_HLCDC_LAYER_SIZE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.size)
101#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.memsize)
102#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.xstride)
103#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pstride)
104#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.default_color)
105#define ATMEL_HLCDC_LAYER_CRKEY_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key)
106#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key_mask)
107
108#define ATMEL_HLCDC_LAYER_GENERAL_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.general_config)
109#define ATMEL_HLCDC_LAYER_CRKEY BIT(0)
110#define ATMEL_HLCDC_LAYER_INV BIT(1)
111#define ATMEL_HLCDC_LAYER_ITER2BL BIT(2)
112#define ATMEL_HLCDC_LAYER_ITER BIT(3)
113#define ATMEL_HLCDC_LAYER_REVALPHA BIT(4)
114#define ATMEL_HLCDC_LAYER_GAEN BIT(5)
115#define ATMEL_HLCDC_LAYER_LAEN BIT(6)
116#define ATMEL_HLCDC_LAYER_OVR BIT(7)
117#define ATMEL_HLCDC_LAYER_DMA BIT(8)
118#define ATMEL_HLCDC_LAYER_REP BIT(9)
119#define ATMEL_HLCDC_LAYER_DSTKEY BIT(10)
120#define ATMEL_HLCDC_LAYER_DISCEN BIT(11)
121#define ATMEL_HLCDC_LAYER_GA_SHIFT 16
122#define ATMEL_HLCDC_LAYER_GA_MASK GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT)
123
124#define ATMEL_HLCDC_LAYER_CSC_CFG(p, o) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.csc + o)
125
126#define ATMEL_HLCDC_LAYER_DISC_POS_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_pos)
127
128#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_size)
129
130#define ATMEL_HLCDC_MAX_PLANES 3
131
132#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED BIT(0)
133#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED BIT(1)
134#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE BIT(2)
135#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN BIT(3)
136
137/**
138 * Atmel HLCDC Layer registers layout structure
139 *
140 * Each HLCDC layer has its own register organization and a given register
141 * can be placed differently on 2 different layers depending on its
142 * capabilities.
143 * This structure stores common registers layout for a given layer and is
144 * used by HLCDC layer code to choose the appropriate register to write to
145 * or to read from.
146 *
147 * For all fields, a value of zero means "unsupported".
148 *
149 * See Atmel's datasheet for a detailled description of these registers.
150 *
151 * @xstride: xstride registers
152 * @pstride: pstride registers
153 * @pos: position register
154 * @size: displayed size register
155 * @memsize: memory size register
156 * @default_color: default color register
157 * @chroma_key: chroma key register
158 * @chroma_key_mask: chroma key mask register
159 * @general_config: general layer config register
160 * @disc_pos: discard area position register
161 * @disc_size: discard area size register
162 * @csc: color space conversion register
163 */
164struct atmel_hlcdc_layer_cfg_layout {
165 int xstride[ATMEL_HLCDC_MAX_PLANES];
166 int pstride[ATMEL_HLCDC_MAX_PLANES];
167 int pos;
168 int size;
169 int memsize;
170 int default_color;
171 int chroma_key;
172 int chroma_key_mask;
173 int general_config;
174 int disc_pos;
175 int disc_size;
176 int csc;
177};
178
179/**
180 * Atmel HLCDC framebuffer flip structure
181 *
182 * This structure is allocated when someone asked for a layer update (most
183 * likely a DRM plane update, either primary, overlay or cursor plane) and
184 * released when the layer do not need to reference the framebuffer object
185 * anymore (i.e. the layer was disabled or updated).
186 *
187 * @dscrs: DMA descriptors
188 * @fb: the referenced framebuffer object
189 * @ngems: number of GEM objects referenced by the fb element
190 * @status: fb flip operation status
191 */
192struct atmel_hlcdc_layer_fb_flip {
193 struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
194 struct drm_flip_task *task;
195 struct drm_framebuffer *fb;
196 int ngems;
197 u32 status;
198};
199
200/**
201 * Atmel HLCDC DMA descriptor structure
202 *
203 * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
204 *
205 * The structure fields must remain in this specific order, because they're
206 * used by the HLCDC DMA engine, which expect them in this order.
207 * HLCDC DMA descriptors must be aligned on 64 bits.
208 *
209 * @addr: buffer DMA address
210 * @ctrl: DMA transfer options
211 * @next: next DMA descriptor to fetch
212 * @gem_flip: the attached gem_flip operation
213 */
214struct atmel_hlcdc_dma_channel_dscr {
215 dma_addr_t addr;
216 u32 ctrl;
217 dma_addr_t next;
218 u32 status;
219} __aligned(sizeof(u64));
220
221/**
222 * Atmel HLCDC layer types
223 */
224enum atmel_hlcdc_layer_type {
225 ATMEL_HLCDC_BASE_LAYER,
226 ATMEL_HLCDC_OVERLAY_LAYER,
227 ATMEL_HLCDC_CURSOR_LAYER,
228 ATMEL_HLCDC_PP_LAYER,
229};
230
231/**
232 * Atmel HLCDC Supported formats structure
233 *
234 * This structure list all the formats supported by a given layer.
235 *
236 * @nformats: number of supported formats
237 * @formats: supported formats
238 */
239struct atmel_hlcdc_formats {
240 int nformats;
241 uint32_t *formats;
242};
243
244/**
245 * Atmel HLCDC Layer description structure
246 *
247 * This structure describe the capabilities provided by a given layer.
248 *
249 * @name: layer name
250 * @type: layer type
251 * @id: layer id
252 * @regs_offset: offset of the layer registers from the HLCDC registers base
253 * @nconfigs: number of config registers provided by this layer
254 * @formats: supported formats
255 * @layout: config registers layout
256 * @max_width: maximum width supported by this layer (0 means unlimited)
257 * @max_height: maximum height supported by this layer (0 means unlimited)
258 */
259struct atmel_hlcdc_layer_desc {
260 const char *name;
261 enum atmel_hlcdc_layer_type type;
262 int id;
263 int regs_offset;
264 int nconfigs;
265 struct atmel_hlcdc_formats *formats;
266 struct atmel_hlcdc_layer_cfg_layout layout;
267 int max_width;
268 int max_height;
269};
270
271/**
272 * Atmel HLCDC Layer Update Slot structure
273 *
274 * This structure stores layer update requests to be applied on next frame.
275 * This is the base structure behind the atomic layer update infrastructure.
276 *
277 * Atomic layer update provides a way to update all layer's parameters
278 * simultaneously. This is needed to avoid incompatible sequential updates
279 * like this one:
280 * 1) update layer format from RGB888 (1 plane/buffer) to YUV422
281 * (2 planes/buffers)
282 * 2) the format update is applied but the DMA channel for the second
283 * plane/buffer is not enabled
284 * 3) enable the DMA channel for the second plane
285 *
286 * @fb_flip: fb_flip object
287 * @updated_configs: bitmask used to record modified configs
288 * @configs: new config values
289 */
290struct atmel_hlcdc_layer_update_slot {
291 struct atmel_hlcdc_layer_fb_flip *fb_flip;
292 unsigned long *updated_configs;
293 u32 *configs;
294};
295
296/**
297 * Atmel HLCDC Layer Update structure
298 *
299 * This structure provides a way to queue layer update requests.
300 *
301 * At a given time there is at most:
302 * - one pending update request, which means the update request has been
303 * committed (or validated) and is waiting for the DMA channel(s) to be
304 * available
305 * - one request being prepared, which means someone started a layer update
306 * but has not committed it yet. There cannot be more than one started
307 * request, because the update lock is taken when starting a layer update
308 * and release when committing or rolling back the request.
309 *
310 * @slots: update slots. One is used for pending request and the other one
311 * for started update request
312 * @pending: the pending slot index or -1 if no request is pending
313 * @next: the started update slot index or -1 no update has been started
314 */
315struct atmel_hlcdc_layer_update {
316 struct atmel_hlcdc_layer_update_slot slots[2];
317 int pending;
318 int next;
319};
320
321enum atmel_hlcdc_layer_dma_channel_status {
322 ATMEL_HLCDC_LAYER_DISABLED,
323 ATMEL_HLCDC_LAYER_ENABLED,
324 ATMEL_HLCDC_LAYER_DISABLING,
325};
326
327/**
328 * Atmel HLCDC Layer DMA channel structure
329 *
330 * This structure stores information on the DMA channel associated to a
331 * given layer.
332 *
333 * @status: DMA channel status
334 * @cur: current framebuffer
335 * @queue: next framebuffer
336 * @dscrs: allocated DMA descriptors
337 */
338struct atmel_hlcdc_layer_dma_channel {
339 enum atmel_hlcdc_layer_dma_channel_status status;
340 struct atmel_hlcdc_layer_fb_flip *cur;
341 struct atmel_hlcdc_layer_fb_flip *queue;
342 struct atmel_hlcdc_dma_channel_dscr *dscrs;
343};
344
345/**
346 * Atmel HLCDC Layer structure
347 *
348 * This structure stores information on the layer instance.
349 *
350 * @desc: layer description
351 * @max_planes: maximum planes/buffers that can be associated with this layer.
352 * This depends on the supported formats.
353 * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
354 * @dma: dma channel
355 * @gc: fb flip garbage collector
356 * @update: update handler
357 * @lock: layer lock
358 */
359struct atmel_hlcdc_layer {
360 const struct atmel_hlcdc_layer_desc *desc;
361 int max_planes;
362 struct atmel_hlcdc *hlcdc;
363 struct workqueue_struct *wq;
364 struct drm_flip_work gc;
365 struct atmel_hlcdc_layer_dma_channel dma;
366 struct atmel_hlcdc_layer_update update;
367 spinlock_t lock;
368};
369
370void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer);
371
372int atmel_hlcdc_layer_init(struct drm_device *dev,
373 struct atmel_hlcdc_layer *layer,
374 const struct atmel_hlcdc_layer_desc *desc);
375
376void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
377 struct atmel_hlcdc_layer *layer);
378
379int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
380
381int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer);
382
383void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
384 u32 mask, u32 val);
385
386void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
387 struct drm_framebuffer *fb,
388 unsigned int *offsets);
389
390void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
391 void (*finished)(void *data),
392 void *finished_data);
393
394void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer);
395
396void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer);
397
398#endif /* DRM_ATMEL_HLCDC_LAYER_H */
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
new file mode 100644
index 000000000000..c402192362c5
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
@@ -0,0 +1,319 @@
1/*
2 * Copyright (C) 2014 Traphandler
3 * Copyright (C) 2014 Free Electrons
4 * Copyright (C) 2014 Atmel
5 *
6 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
7 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published by
11 * the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <linux/of_graph.h>
23
24#include <drm/drmP.h>
25#include <drm/drm_panel.h>
26
27#include "atmel_hlcdc_dc.h"
28
29/**
30 * Atmel HLCDC RGB output mode
31 */
32enum atmel_hlcdc_connector_rgb_mode {
33 ATMEL_HLCDC_CONNECTOR_RGB444,
34 ATMEL_HLCDC_CONNECTOR_RGB565,
35 ATMEL_HLCDC_CONNECTOR_RGB666,
36 ATMEL_HLCDC_CONNECTOR_RGB888,
37};
38
39/**
40 * Atmel HLCDC RGB connector structure
41 *
42 * This structure stores RGB slave device information.
43 *
44 * @connector: DRM connector
45 * @encoder: DRM encoder
46 * @dc: pointer to the atmel_hlcdc_dc structure
47 * @dpms: current DPMS mode
48 */
49struct atmel_hlcdc_rgb_output {
50 struct drm_connector connector;
51 struct drm_encoder encoder;
52 struct atmel_hlcdc_dc *dc;
53 int dpms;
54};
55
56static inline struct atmel_hlcdc_rgb_output *
57drm_connector_to_atmel_hlcdc_rgb_output(struct drm_connector *connector)
58{
59 return container_of(connector, struct atmel_hlcdc_rgb_output,
60 connector);
61}
62
63static inline struct atmel_hlcdc_rgb_output *
64drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder)
65{
66 return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
67}
68
69/**
70 * Atmel HLCDC Panel device structure
71 *
72 * This structure is specialization of the slave device structure to
73 * interface with drm panels.
74 *
75 * @base: base slave device fields
76 * @panel: drm panel attached to this slave device
77 */
78struct atmel_hlcdc_panel {
79 struct atmel_hlcdc_rgb_output base;
80 struct drm_panel *panel;
81};
82
83static inline struct atmel_hlcdc_panel *
84atmel_hlcdc_rgb_output_to_panel(struct atmel_hlcdc_rgb_output *output)
85{
86 return container_of(output, struct atmel_hlcdc_panel, base);
87}
88
89static void atmel_hlcdc_panel_encoder_dpms(struct drm_encoder *encoder,
90 int mode)
91{
92 struct atmel_hlcdc_rgb_output *rgb =
93 drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
94 struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
95
96 if (mode != DRM_MODE_DPMS_ON)
97 mode = DRM_MODE_DPMS_OFF;
98
99 if (mode == rgb->dpms)
100 return;
101
102 if (mode != DRM_MODE_DPMS_ON)
103 drm_panel_disable(panel->panel);
104 else
105 drm_panel_enable(panel->panel);
106
107 rgb->dpms = mode;
108}
109
110static bool
111atmel_hlcdc_panel_encoder_mode_fixup(struct drm_encoder *encoder,
112 const struct drm_display_mode *mode,
113 struct drm_display_mode *adjusted)
114{
115 return true;
116}
117
118static void atmel_hlcdc_panel_encoder_prepare(struct drm_encoder *encoder)
119{
120 atmel_hlcdc_panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
121}
122
123static void atmel_hlcdc_panel_encoder_commit(struct drm_encoder *encoder)
124{
125 atmel_hlcdc_panel_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
126}
127
128static void
129atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder,
130 struct drm_display_mode *mode,
131 struct drm_display_mode *adjusted)
132{
133 struct atmel_hlcdc_rgb_output *rgb =
134 drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
135 struct drm_display_info *info = &rgb->connector.display_info;
136 unsigned int cfg;
137
138 cfg = 0;
139
140 if (info->num_bus_formats) {
141 switch (info->bus_formats[0]) {
142 case MEDIA_BUS_FMT_RGB666_1X18:
143 cfg |= ATMEL_HLCDC_CONNECTOR_RGB666 << 8;
144 break;
145 case MEDIA_BUS_FMT_RGB888_1X24:
146 cfg |= ATMEL_HLCDC_CONNECTOR_RGB888 << 8;
147 break;
148 default:
149 break;
150 }
151 }
152
153 regmap_update_bits(rgb->dc->hlcdc->regmap, ATMEL_HLCDC_CFG(5),
154 ATMEL_HLCDC_MODE_MASK,
155 cfg);
156}
157
158static struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = {
159 .dpms = atmel_hlcdc_panel_encoder_dpms,
160 .mode_fixup = atmel_hlcdc_panel_encoder_mode_fixup,
161 .prepare = atmel_hlcdc_panel_encoder_prepare,
162 .commit = atmel_hlcdc_panel_encoder_commit,
163 .mode_set = atmel_hlcdc_rgb_encoder_mode_set,
164};
165
166static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder)
167{
168 drm_encoder_cleanup(encoder);
169 memset(encoder, 0, sizeof(*encoder));
170}
171
172static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = {
173 .destroy = atmel_hlcdc_rgb_encoder_destroy,
174};
175
176static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector)
177{
178 struct atmel_hlcdc_rgb_output *rgb =
179 drm_connector_to_atmel_hlcdc_rgb_output(connector);
180 struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
181
182 return panel->panel->funcs->get_modes(panel->panel);
183}
184
185static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector,
186 struct drm_display_mode *mode)
187{
188 struct atmel_hlcdc_rgb_output *rgb =
189 drm_connector_to_atmel_hlcdc_rgb_output(connector);
190
191 return atmel_hlcdc_dc_mode_valid(rgb->dc, mode);
192}
193
194
195
196static struct drm_encoder *
197atmel_hlcdc_rgb_best_encoder(struct drm_connector *connector)
198{
199 struct atmel_hlcdc_rgb_output *rgb =
200 drm_connector_to_atmel_hlcdc_rgb_output(connector);
201
202 return &rgb->encoder;
203}
204
205static struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helper_funcs = {
206 .get_modes = atmel_hlcdc_panel_get_modes,
207 .mode_valid = atmel_hlcdc_rgb_mode_valid,
208 .best_encoder = atmel_hlcdc_rgb_best_encoder,
209};
210
211static enum drm_connector_status
212atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force)
213{
214 return connector_status_connected;
215}
216
217static void
218atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector)
219{
220 struct atmel_hlcdc_rgb_output *rgb =
221 drm_connector_to_atmel_hlcdc_rgb_output(connector);
222 struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb);
223
224 drm_panel_detach(panel->panel);
225 drm_connector_cleanup(connector);
226}
227
228static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = {
229 .dpms = drm_helper_connector_dpms,
230 .detect = atmel_hlcdc_panel_connector_detect,
231 .fill_modes = drm_helper_probe_single_connector_modes,
232 .destroy = atmel_hlcdc_panel_connector_destroy,
233};
234
235static int atmel_hlcdc_create_panel_output(struct drm_device *dev,
236 struct of_endpoint *ep)
237{
238 struct atmel_hlcdc_dc *dc = dev->dev_private;
239 struct device_node *np;
240 struct drm_panel *p = NULL;
241 struct atmel_hlcdc_panel *panel;
242 int ret;
243
244 np = of_graph_get_remote_port_parent(ep->local_node);
245 if (!np)
246 return -EINVAL;
247
248 p = of_drm_find_panel(np);
249 of_node_put(np);
250
251 if (!p)
252 return -EPROBE_DEFER;
253
254 panel = devm_kzalloc(dev->dev, sizeof(*panel), GFP_KERNEL);
255 if (!panel)
256 return -EINVAL;
257
258 panel->base.dpms = DRM_MODE_DPMS_OFF;
259
260 panel->base.dc = dc;
261
262 drm_encoder_helper_add(&panel->base.encoder,
263 &atmel_hlcdc_panel_encoder_helper_funcs);
264 ret = drm_encoder_init(dev, &panel->base.encoder,
265 &atmel_hlcdc_panel_encoder_funcs,
266 DRM_MODE_ENCODER_LVDS);
267 if (ret)
268 return ret;
269
270 panel->base.connector.dpms = DRM_MODE_DPMS_OFF;
271 panel->base.connector.polled = DRM_CONNECTOR_POLL_CONNECT;
272 drm_connector_helper_add(&panel->base.connector,
273 &atmel_hlcdc_panel_connector_helper_funcs);
274 ret = drm_connector_init(dev, &panel->base.connector,
275 &atmel_hlcdc_panel_connector_funcs,
276 DRM_MODE_CONNECTOR_LVDS);
277 if (ret)
278 goto err_encoder_cleanup;
279
280 drm_mode_connector_attach_encoder(&panel->base.connector,
281 &panel->base.encoder);
282 panel->base.encoder.possible_crtcs = 0x1;
283
284 drm_panel_attach(p, &panel->base.connector);
285 panel->panel = p;
286
287 return 0;
288
289err_encoder_cleanup:
290 drm_encoder_cleanup(&panel->base.encoder);
291
292 return ret;
293}
294
295int atmel_hlcdc_create_outputs(struct drm_device *dev)
296{
297 struct device_node *port_np, *np;
298 struct of_endpoint ep;
299 int ret;
300
301 port_np = of_get_child_by_name(dev->dev->of_node, "port");
302 if (!port_np)
303 return -EINVAL;
304
305 np = of_get_child_by_name(port_np, "endpoint");
306 of_node_put(port_np);
307
308 if (!np)
309 return -EINVAL;
310
311 ret = of_graph_parse_endpoint(np, &ep);
312 of_node_put(port_np);
313
314 if (ret)
315 return ret;
316
317 /* We currently only support panel output */
318 return atmel_hlcdc_create_panel_output(dev, &ep);
319}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
new file mode 100644
index 000000000000..c5892dcfd745
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
@@ -0,0 +1,856 @@
1/*
2 * Copyright (C) 2014 Free Electrons
3 * Copyright (C) 2014 Atmel
4 *
5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "atmel_hlcdc_dc.h"
21
22#define SUBPIXEL_MASK 0xffff
23
24static uint32_t rgb_formats[] = {
25 DRM_FORMAT_XRGB4444,
26 DRM_FORMAT_ARGB4444,
27 DRM_FORMAT_RGBA4444,
28 DRM_FORMAT_ARGB1555,
29 DRM_FORMAT_RGB565,
30 DRM_FORMAT_RGB888,
31 DRM_FORMAT_XRGB8888,
32 DRM_FORMAT_ARGB8888,
33 DRM_FORMAT_RGBA8888,
34};
35
36struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
37 .formats = rgb_formats,
38 .nformats = ARRAY_SIZE(rgb_formats),
39};
40
41static uint32_t rgb_and_yuv_formats[] = {
42 DRM_FORMAT_XRGB4444,
43 DRM_FORMAT_ARGB4444,
44 DRM_FORMAT_RGBA4444,
45 DRM_FORMAT_ARGB1555,
46 DRM_FORMAT_RGB565,
47 DRM_FORMAT_RGB888,
48 DRM_FORMAT_XRGB8888,
49 DRM_FORMAT_ARGB8888,
50 DRM_FORMAT_RGBA8888,
51 DRM_FORMAT_AYUV,
52 DRM_FORMAT_YUYV,
53 DRM_FORMAT_UYVY,
54 DRM_FORMAT_YVYU,
55 DRM_FORMAT_VYUY,
56 DRM_FORMAT_NV21,
57 DRM_FORMAT_NV61,
58 DRM_FORMAT_YUV422,
59 DRM_FORMAT_YUV420,
60};
61
62struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
63 .formats = rgb_and_yuv_formats,
64 .nformats = ARRAY_SIZE(rgb_and_yuv_formats),
65};
66
67static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
68{
69 switch (format) {
70 case DRM_FORMAT_XRGB4444:
71 *mode = ATMEL_HLCDC_XRGB4444_MODE;
72 break;
73 case DRM_FORMAT_ARGB4444:
74 *mode = ATMEL_HLCDC_ARGB4444_MODE;
75 break;
76 case DRM_FORMAT_RGBA4444:
77 *mode = ATMEL_HLCDC_RGBA4444_MODE;
78 break;
79 case DRM_FORMAT_RGB565:
80 *mode = ATMEL_HLCDC_RGB565_MODE;
81 break;
82 case DRM_FORMAT_RGB888:
83 *mode = ATMEL_HLCDC_RGB888_MODE;
84 break;
85 case DRM_FORMAT_ARGB1555:
86 *mode = ATMEL_HLCDC_ARGB1555_MODE;
87 break;
88 case DRM_FORMAT_XRGB8888:
89 *mode = ATMEL_HLCDC_XRGB8888_MODE;
90 break;
91 case DRM_FORMAT_ARGB8888:
92 *mode = ATMEL_HLCDC_ARGB8888_MODE;
93 break;
94 case DRM_FORMAT_RGBA8888:
95 *mode = ATMEL_HLCDC_RGBA8888_MODE;
96 break;
97 case DRM_FORMAT_AYUV:
98 *mode = ATMEL_HLCDC_AYUV_MODE;
99 break;
100 case DRM_FORMAT_YUYV:
101 *mode = ATMEL_HLCDC_YUYV_MODE;
102 break;
103 case DRM_FORMAT_UYVY:
104 *mode = ATMEL_HLCDC_UYVY_MODE;
105 break;
106 case DRM_FORMAT_YVYU:
107 *mode = ATMEL_HLCDC_YVYU_MODE;
108 break;
109 case DRM_FORMAT_VYUY:
110 *mode = ATMEL_HLCDC_VYUY_MODE;
111 break;
112 case DRM_FORMAT_NV21:
113 *mode = ATMEL_HLCDC_NV21_MODE;
114 break;
115 case DRM_FORMAT_NV61:
116 *mode = ATMEL_HLCDC_NV61_MODE;
117 break;
118 case DRM_FORMAT_YUV420:
119 *mode = ATMEL_HLCDC_YUV420_MODE;
120 break;
121 case DRM_FORMAT_YUV422:
122 *mode = ATMEL_HLCDC_YUV422_MODE;
123 break;
124 default:
125 return -ENOTSUPP;
126 }
127
128 return 0;
129}
130
131static bool atmel_hlcdc_format_embedds_alpha(u32 format)
132{
133 int i;
134
135 for (i = 0; i < sizeof(format); i++) {
136 char tmp = (format >> (8 * i)) & 0xff;
137
138 if (tmp == 'A')
139 return true;
140 }
141
142 return false;
143}
144
145static u32 heo_downscaling_xcoef[] = {
146 0x11343311,
147 0x000000f7,
148 0x1635300c,
149 0x000000f9,
150 0x1b362c08,
151 0x000000fb,
152 0x1f372804,
153 0x000000fe,
154 0x24382400,
155 0x00000000,
156 0x28371ffe,
157 0x00000004,
158 0x2c361bfb,
159 0x00000008,
160 0x303516f9,
161 0x0000000c,
162};
163
164static u32 heo_downscaling_ycoef[] = {
165 0x00123737,
166 0x00173732,
167 0x001b382d,
168 0x001f3928,
169 0x00243824,
170 0x0028391f,
171 0x002d381b,
172 0x00323717,
173};
174
175static u32 heo_upscaling_xcoef[] = {
176 0xf74949f7,
177 0x00000000,
178 0xf55f33fb,
179 0x000000fe,
180 0xf5701efe,
181 0x000000ff,
182 0xf87c0dff,
183 0x00000000,
184 0x00800000,
185 0x00000000,
186 0x0d7cf800,
187 0x000000ff,
188 0x1e70f5ff,
189 0x000000fe,
190 0x335ff5fe,
191 0x000000fb,
192};
193
194static u32 heo_upscaling_ycoef[] = {
195 0x00004040,
196 0x00075920,
197 0x00056f0c,
198 0x00027b03,
199 0x00008000,
200 0x00037b02,
201 0x000c6f05,
202 0x00205907,
203};
204
205static void
206atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
207 struct atmel_hlcdc_plane_update_req *req)
208{
209 const struct atmel_hlcdc_layer_cfg_layout *layout =
210 &plane->layer.desc->layout;
211
212 if (layout->size)
213 atmel_hlcdc_layer_update_cfg(&plane->layer,
214 layout->size,
215 0xffffffff,
216 (req->crtc_w - 1) |
217 ((req->crtc_h - 1) << 16));
218
219 if (layout->memsize)
220 atmel_hlcdc_layer_update_cfg(&plane->layer,
221 layout->memsize,
222 0xffffffff,
223 (req->src_w - 1) |
224 ((req->src_h - 1) << 16));
225
226 if (layout->pos)
227 atmel_hlcdc_layer_update_cfg(&plane->layer,
228 layout->pos,
229 0xffffffff,
230 req->crtc_x |
231 (req->crtc_y << 16));
232
233 /* TODO: rework the rescaling part */
234 if (req->crtc_w != req->src_w || req->crtc_h != req->src_h) {
235 u32 factor_reg = 0;
236
237 if (req->crtc_w != req->src_w) {
238 int i;
239 u32 factor;
240 u32 *coeff_tab = heo_upscaling_xcoef;
241 u32 max_memsize;
242
243 if (req->crtc_w < req->src_w)
244 coeff_tab = heo_downscaling_xcoef;
245 for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
246 atmel_hlcdc_layer_update_cfg(&plane->layer,
247 17 + i,
248 0xffffffff,
249 coeff_tab[i]);
250 factor = ((8 * 256 * req->src_w) - (256 * 4)) /
251 req->crtc_w;
252 factor++;
253 max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
254 2048;
255 if (max_memsize > req->src_w)
256 factor--;
257 factor_reg |= factor | 0x80000000;
258 }
259
260 if (req->crtc_h != req->src_h) {
261 int i;
262 u32 factor;
263 u32 *coeff_tab = heo_upscaling_ycoef;
264 u32 max_memsize;
265
266 if (req->crtc_w < req->src_w)
267 coeff_tab = heo_downscaling_ycoef;
268 for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
269 atmel_hlcdc_layer_update_cfg(&plane->layer,
270 33 + i,
271 0xffffffff,
272 coeff_tab[i]);
273 factor = ((8 * 256 * req->src_w) - (256 * 4)) /
274 req->crtc_w;
275 factor++;
276 max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
277 2048;
278 if (max_memsize > req->src_w)
279 factor--;
280 factor_reg |= (factor << 16) | 0x80000000;
281 }
282
283 atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
284 factor_reg);
285 }
286}
287
288static void
289atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
290 struct atmel_hlcdc_plane_update_req *req)
291{
292 const struct atmel_hlcdc_layer_cfg_layout *layout =
293 &plane->layer.desc->layout;
294 unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
295
296 if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
297 cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
298 ATMEL_HLCDC_LAYER_ITER;
299
300 if (atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format))
301 cfg |= ATMEL_HLCDC_LAYER_LAEN;
302 else
303 cfg |= ATMEL_HLCDC_LAYER_GAEN;
304 }
305
306 atmel_hlcdc_layer_update_cfg(&plane->layer,
307 ATMEL_HLCDC_LAYER_DMA_CFG_ID,
308 ATMEL_HLCDC_LAYER_DMA_BLEN_MASK,
309 ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16);
310
311 atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
312 ATMEL_HLCDC_LAYER_ITER2BL |
313 ATMEL_HLCDC_LAYER_ITER |
314 ATMEL_HLCDC_LAYER_GAEN |
315 ATMEL_HLCDC_LAYER_LAEN |
316 ATMEL_HLCDC_LAYER_OVR |
317 ATMEL_HLCDC_LAYER_DMA, cfg);
318}
319
320static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
321 struct atmel_hlcdc_plane_update_req *req)
322{
323 u32 cfg;
324 int ret;
325
326 ret = atmel_hlcdc_format_to_plane_mode(req->fb->pixel_format, &cfg);
327 if (ret)
328 return;
329
330 if ((req->fb->pixel_format == DRM_FORMAT_YUV422 ||
331 req->fb->pixel_format == DRM_FORMAT_NV61) &&
332 (plane->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))))
333 cfg |= ATMEL_HLCDC_YUV422ROT;
334
335 atmel_hlcdc_layer_update_cfg(&plane->layer,
336 ATMEL_HLCDC_LAYER_FORMAT_CFG_ID,
337 0xffffffff,
338 cfg);
339
340 /*
341 * Rotation optimization is not working on RGB888 (rotation is still
342 * working but without any optimization).
343 */
344 if (req->fb->pixel_format == DRM_FORMAT_RGB888)
345 cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS;
346 else
347 cfg = 0;
348
349 atmel_hlcdc_layer_update_cfg(&plane->layer,
350 ATMEL_HLCDC_LAYER_DMA_CFG_ID,
351 ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg);
352}
353
354static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
355 struct atmel_hlcdc_plane_update_req *req)
356{
357 struct atmel_hlcdc_layer *layer = &plane->layer;
358 const struct atmel_hlcdc_layer_cfg_layout *layout =
359 &layer->desc->layout;
360 int i;
361
362 atmel_hlcdc_layer_update_set_fb(&plane->layer, req->fb, req->offsets);
363
364 for (i = 0; i < req->nplanes; i++) {
365 if (layout->xstride[i]) {
366 atmel_hlcdc_layer_update_cfg(&plane->layer,
367 layout->xstride[i],
368 0xffffffff,
369 req->xstride[i]);
370 }
371
372 if (layout->pstride[i]) {
373 atmel_hlcdc_layer_update_cfg(&plane->layer,
374 layout->pstride[i],
375 0xffffffff,
376 req->pstride[i]);
377 }
378 }
379}
380
381static int atmel_hlcdc_plane_check_update_req(struct drm_plane *p,
382 struct atmel_hlcdc_plane_update_req *req,
383 const struct drm_display_mode *mode)
384{
385 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
386 const struct atmel_hlcdc_layer_cfg_layout *layout =
387 &plane->layer.desc->layout;
388
389 if (!layout->size &&
390 (mode->hdisplay != req->crtc_w ||
391 mode->vdisplay != req->crtc_h))
392 return -EINVAL;
393
394 if (plane->layer.desc->max_height &&
395 req->crtc_h > plane->layer.desc->max_height)
396 return -EINVAL;
397
398 if (plane->layer.desc->max_width &&
399 req->crtc_w > plane->layer.desc->max_width)
400 return -EINVAL;
401
402 if ((req->crtc_h != req->src_h || req->crtc_w != req->src_w) &&
403 (!layout->memsize ||
404 atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format)))
405 return -EINVAL;
406
407 if (req->crtc_x < 0 || req->crtc_y < 0)
408 return -EINVAL;
409
410 if (req->crtc_w + req->crtc_x > mode->hdisplay ||
411 req->crtc_h + req->crtc_y > mode->vdisplay)
412 return -EINVAL;
413
414 return 0;
415}
416
417int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
418 struct atmel_hlcdc_plane_update_req *req,
419 const struct drm_display_mode *mode)
420{
421 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
422 unsigned int patched_crtc_w;
423 unsigned int patched_crtc_h;
424 unsigned int patched_src_w;
425 unsigned int patched_src_h;
426 unsigned int tmp;
427 int x_offset = 0;
428 int y_offset = 0;
429 int hsub = 1;
430 int vsub = 1;
431 int i;
432
433 if ((req->src_x | req->src_y | req->src_w | req->src_h) &
434 SUBPIXEL_MASK)
435 return -EINVAL;
436
437 req->src_x >>= 16;
438 req->src_y >>= 16;
439 req->src_w >>= 16;
440 req->src_h >>= 16;
441
442 req->nplanes = drm_format_num_planes(req->fb->pixel_format);
443 if (req->nplanes > ATMEL_HLCDC_MAX_PLANES)
444 return -EINVAL;
445
446 /*
447 * Swap width and size in case of 90 or 270 degrees rotation
448 */
449 if (plane->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) {
450 tmp = req->crtc_w;
451 req->crtc_w = req->crtc_h;
452 req->crtc_h = tmp;
453 tmp = req->src_w;
454 req->src_w = req->src_h;
455 req->src_h = tmp;
456 }
457
458 if (req->crtc_x + req->crtc_w > mode->hdisplay)
459 patched_crtc_w = mode->hdisplay - req->crtc_x;
460 else
461 patched_crtc_w = req->crtc_w;
462
463 if (req->crtc_x < 0) {
464 patched_crtc_w += req->crtc_x;
465 x_offset = -req->crtc_x;
466 req->crtc_x = 0;
467 }
468
469 if (req->crtc_y + req->crtc_h > mode->vdisplay)
470 patched_crtc_h = mode->vdisplay - req->crtc_y;
471 else
472 patched_crtc_h = req->crtc_h;
473
474 if (req->crtc_y < 0) {
475 patched_crtc_h += req->crtc_y;
476 y_offset = -req->crtc_y;
477 req->crtc_y = 0;
478 }
479
480 patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * req->src_w,
481 req->crtc_w);
482 patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * req->src_h,
483 req->crtc_h);
484
485 hsub = drm_format_horz_chroma_subsampling(req->fb->pixel_format);
486 vsub = drm_format_vert_chroma_subsampling(req->fb->pixel_format);
487
488 for (i = 0; i < req->nplanes; i++) {
489 unsigned int offset = 0;
490 int xdiv = i ? hsub : 1;
491 int ydiv = i ? vsub : 1;
492
493 req->bpp[i] = drm_format_plane_cpp(req->fb->pixel_format, i);
494 if (!req->bpp[i])
495 return -EINVAL;
496
497 switch (plane->rotation & 0xf) {
498 case BIT(DRM_ROTATE_90):
499 offset = ((y_offset + req->src_y + patched_src_w - 1) /
500 ydiv) * req->fb->pitches[i];
501 offset += ((x_offset + req->src_x) / xdiv) *
502 req->bpp[i];
503 req->xstride[i] = ((patched_src_w - 1) / ydiv) *
504 req->fb->pitches[i];
505 req->pstride[i] = -req->fb->pitches[i] - req->bpp[i];
506 break;
507 case BIT(DRM_ROTATE_180):
508 offset = ((y_offset + req->src_y + patched_src_h - 1) /
509 ydiv) * req->fb->pitches[i];
510 offset += ((x_offset + req->src_x + patched_src_w - 1) /
511 xdiv) * req->bpp[i];
512 req->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) *
513 req->bpp[i]) - req->fb->pitches[i];
514 req->pstride[i] = -2 * req->bpp[i];
515 break;
516 case BIT(DRM_ROTATE_270):
517 offset = ((y_offset + req->src_y) / ydiv) *
518 req->fb->pitches[i];
519 offset += ((x_offset + req->src_x + patched_src_h - 1) /
520 xdiv) * req->bpp[i];
521 req->xstride[i] = -(((patched_src_w - 1) / ydiv) *
522 req->fb->pitches[i]) -
523 (2 * req->bpp[i]);
524 req->pstride[i] = req->fb->pitches[i] - req->bpp[i];
525 break;
526 case BIT(DRM_ROTATE_0):
527 default:
528 offset = ((y_offset + req->src_y) / ydiv) *
529 req->fb->pitches[i];
530 offset += ((x_offset + req->src_x) / xdiv) *
531 req->bpp[i];
532 req->xstride[i] = req->fb->pitches[i] -
533 ((patched_src_w / xdiv) *
534 req->bpp[i]);
535 req->pstride[i] = 0;
536 break;
537 }
538
539 req->offsets[i] = offset + req->fb->offsets[i];
540 }
541
542 req->src_w = patched_src_w;
543 req->src_h = patched_src_h;
544 req->crtc_w = patched_crtc_w;
545 req->crtc_h = patched_crtc_h;
546
547 return atmel_hlcdc_plane_check_update_req(p, req, mode);
548}
549
550int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
551 struct atmel_hlcdc_plane_update_req *req)
552{
553 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
554 int ret;
555
556 ret = atmel_hlcdc_layer_update_start(&plane->layer);
557 if (ret)
558 return ret;
559
560 atmel_hlcdc_plane_update_pos_and_size(plane, req);
561 atmel_hlcdc_plane_update_general_settings(plane, req);
562 atmel_hlcdc_plane_update_format(plane, req);
563 atmel_hlcdc_plane_update_buffers(plane, req);
564
565 atmel_hlcdc_layer_update_commit(&plane->layer);
566
567 return 0;
568}
569
570int atmel_hlcdc_plane_update_with_mode(struct drm_plane *p,
571 struct drm_crtc *crtc,
572 struct drm_framebuffer *fb,
573 int crtc_x, int crtc_y,
574 unsigned int crtc_w,
575 unsigned int crtc_h,
576 uint32_t src_x, uint32_t src_y,
577 uint32_t src_w, uint32_t src_h,
578 const struct drm_display_mode *mode)
579{
580 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
581 struct atmel_hlcdc_plane_update_req req;
582 int ret = 0;
583
584 memset(&req, 0, sizeof(req));
585 req.crtc_x = crtc_x;
586 req.crtc_y = crtc_y;
587 req.crtc_w = crtc_w;
588 req.crtc_h = crtc_h;
589 req.src_x = src_x;
590 req.src_y = src_y;
591 req.src_w = src_w;
592 req.src_h = src_h;
593 req.fb = fb;
594
595 ret = atmel_hlcdc_plane_prepare_update_req(&plane->base, &req, mode);
596 if (ret)
597 return ret;
598
599 if (!req.crtc_h || !req.crtc_w)
600 return atmel_hlcdc_layer_disable(&plane->layer);
601
602 return atmel_hlcdc_plane_apply_update_req(&plane->base, &req);
603}
604
605static int atmel_hlcdc_plane_update(struct drm_plane *p,
606 struct drm_crtc *crtc,
607 struct drm_framebuffer *fb,
608 int crtc_x, int crtc_y,
609 unsigned int crtc_w, unsigned int crtc_h,
610 uint32_t src_x, uint32_t src_y,
611 uint32_t src_w, uint32_t src_h)
612{
613 return atmel_hlcdc_plane_update_with_mode(p, crtc, fb, crtc_x, crtc_y,
614 crtc_w, crtc_h, src_x, src_y,
615 src_w, src_h, &crtc->hwmode);
616}
617
618static int atmel_hlcdc_plane_disable(struct drm_plane *p)
619{
620 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
621
622 return atmel_hlcdc_layer_disable(&plane->layer);
623}
624
625static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
626{
627 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
628
629 if (plane->base.fb)
630 drm_framebuffer_unreference(plane->base.fb);
631
632 atmel_hlcdc_layer_cleanup(p->dev, &plane->layer);
633
634 drm_plane_cleanup(p);
635 devm_kfree(p->dev->dev, plane);
636}
637
638static int atmel_hlcdc_plane_set_alpha(struct atmel_hlcdc_plane *plane,
639 u8 alpha)
640{
641 atmel_hlcdc_layer_update_start(&plane->layer);
642 atmel_hlcdc_layer_update_cfg(&plane->layer,
643 plane->layer.desc->layout.general_config,
644 ATMEL_HLCDC_LAYER_GA_MASK,
645 alpha << ATMEL_HLCDC_LAYER_GA_SHIFT);
646 atmel_hlcdc_layer_update_commit(&plane->layer);
647
648 return 0;
649}
650
651static int atmel_hlcdc_plane_set_rotation(struct atmel_hlcdc_plane *plane,
652 unsigned int rotation)
653{
654 plane->rotation = rotation;
655
656 return 0;
657}
658
659static int atmel_hlcdc_plane_set_property(struct drm_plane *p,
660 struct drm_property *property,
661 uint64_t value)
662{
663 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
664 struct atmel_hlcdc_plane_properties *props = plane->properties;
665
666 if (property == props->alpha)
667 atmel_hlcdc_plane_set_alpha(plane, value);
668 else if (property == props->rotation)
669 atmel_hlcdc_plane_set_rotation(plane, value);
670 else
671 return -EINVAL;
672
673 return 0;
674}
675
676static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
677 const struct atmel_hlcdc_layer_desc *desc,
678 struct atmel_hlcdc_plane_properties *props)
679{
680 struct regmap *regmap = plane->layer.hlcdc->regmap;
681
682 if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
683 desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
684 drm_object_attach_property(&plane->base.base,
685 props->alpha, 255);
686
687 /* Set default alpha value */
688 regmap_update_bits(regmap,
689 desc->regs_offset +
690 ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
691 ATMEL_HLCDC_LAYER_GA_MASK,
692 ATMEL_HLCDC_LAYER_GA_MASK);
693 }
694
695 if (desc->layout.xstride && desc->layout.pstride)
696 drm_object_attach_property(&plane->base.base,
697 props->rotation,
698 BIT(DRM_ROTATE_0));
699
700 if (desc->layout.csc) {
701 /*
702 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
703 * userspace modify these factors (using a BLOB property ?).
704 */
705 regmap_write(regmap,
706 desc->regs_offset +
707 ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
708 0x4c900091);
709 regmap_write(regmap,
710 desc->regs_offset +
711 ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
712 0x7a5f5090);
713 regmap_write(regmap,
714 desc->regs_offset +
715 ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
716 0x40040890);
717 }
718}
719
720static struct drm_plane_funcs layer_plane_funcs = {
721 .update_plane = atmel_hlcdc_plane_update,
722 .disable_plane = atmel_hlcdc_plane_disable,
723 .set_property = atmel_hlcdc_plane_set_property,
724 .destroy = atmel_hlcdc_plane_destroy,
725};
726
727static struct atmel_hlcdc_plane *
728atmel_hlcdc_plane_create(struct drm_device *dev,
729 const struct atmel_hlcdc_layer_desc *desc,
730 struct atmel_hlcdc_plane_properties *props)
731{
732 struct atmel_hlcdc_plane *plane;
733 enum drm_plane_type type;
734 int ret;
735
736 plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
737 if (!plane)
738 return ERR_PTR(-ENOMEM);
739
740 ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
741 if (ret)
742 return ERR_PTR(ret);
743
744 if (desc->type == ATMEL_HLCDC_BASE_LAYER)
745 type = DRM_PLANE_TYPE_PRIMARY;
746 else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
747 type = DRM_PLANE_TYPE_CURSOR;
748 else
749 type = DRM_PLANE_TYPE_OVERLAY;
750
751 ret = drm_universal_plane_init(dev, &plane->base, 0,
752 &layer_plane_funcs,
753 desc->formats->formats,
754 desc->formats->nformats, type);
755 if (ret)
756 return ERR_PTR(ret);
757
758 /* Set default property values*/
759 atmel_hlcdc_plane_init_properties(plane, desc, props);
760
761 return plane;
762}
763
764static struct atmel_hlcdc_plane_properties *
765atmel_hlcdc_plane_create_properties(struct drm_device *dev)
766{
767 struct atmel_hlcdc_plane_properties *props;
768
769 props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
770 if (!props)
771 return ERR_PTR(-ENOMEM);
772
773 props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
774 if (!props->alpha)
775 return ERR_PTR(-ENOMEM);
776
777 props->rotation = drm_mode_create_rotation_property(dev,
778 BIT(DRM_ROTATE_0) |
779 BIT(DRM_ROTATE_90) |
780 BIT(DRM_ROTATE_180) |
781 BIT(DRM_ROTATE_270));
782 if (!props->rotation)
783 return ERR_PTR(-ENOMEM);
784
785 return props;
786}
787
788struct atmel_hlcdc_planes *
789atmel_hlcdc_create_planes(struct drm_device *dev)
790{
791 struct atmel_hlcdc_dc *dc = dev->dev_private;
792 struct atmel_hlcdc_plane_properties *props;
793 struct atmel_hlcdc_planes *planes;
794 const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
795 int nlayers = dc->desc->nlayers;
796 int i;
797
798 planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL);
799 if (!planes)
800 return ERR_PTR(-ENOMEM);
801
802 for (i = 0; i < nlayers; i++) {
803 if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
804 planes->noverlays++;
805 }
806
807 if (planes->noverlays) {
808 planes->overlays = devm_kzalloc(dev->dev,
809 planes->noverlays *
810 sizeof(*planes->overlays),
811 GFP_KERNEL);
812 if (!planes->overlays)
813 return ERR_PTR(-ENOMEM);
814 }
815
816 props = atmel_hlcdc_plane_create_properties(dev);
817 if (IS_ERR(props))
818 return ERR_CAST(props);
819
820 planes->noverlays = 0;
821 for (i = 0; i < nlayers; i++) {
822 struct atmel_hlcdc_plane *plane;
823
824 if (descs[i].type == ATMEL_HLCDC_PP_LAYER)
825 continue;
826
827 plane = atmel_hlcdc_plane_create(dev, &descs[i], props);
828 if (IS_ERR(plane))
829 return ERR_CAST(plane);
830
831 plane->properties = props;
832
833 switch (descs[i].type) {
834 case ATMEL_HLCDC_BASE_LAYER:
835 if (planes->primary)
836 return ERR_PTR(-EINVAL);
837 planes->primary = plane;
838 break;
839
840 case ATMEL_HLCDC_OVERLAY_LAYER:
841 planes->overlays[planes->noverlays++] = plane;
842 break;
843
844 case ATMEL_HLCDC_CURSOR_LAYER:
845 if (planes->cursor)
846 return ERR_PTR(-EINVAL);
847 planes->cursor = plane;
848 break;
849
850 default:
851 break;
852 }
853 }
854
855 return planes;
856}