aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2012-08-15 08:59:49 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2013-10-12 05:13:40 -0400
commit96f60e37dc66091bde8d5de136ff6fda09f2d799 (patch)
tree2c2cc30a5ac7339730430369e27d33d4a8dd21ef
parent15c03dd4859ab16f9212238f29dd315654aa94f6 (diff)
DRM: Armada: Add Armada DRM driver
This patch adds support for the pair of LCD controllers on the Marvell Armada 510 SoCs. This driver supports: - multiple contiguous scanout buffers for video and graphics - shm backed cacheable buffer objects for X pixmaps for Vivante GPU acceleration - dual lcd0 and lcd1 crt operation - video overlay on each LCD crt via DRM planes - page flipping of the main scanout buffers - DRM prime for buffer export/import This driver is trivial to extend to other Armada SoCs. Included in this commit is the core driver with no output support; output support is platform and encoder driver dependent. Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> Reviewed-by: Rob Clark <robdclark@gmail.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/armada/Kconfig15
-rw-r--r--drivers/gpu/drm/armada/Makefile7
-rw-r--r--drivers/gpu/drm/armada/armada_510.c86
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.c861
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.h74
-rw-r--r--drivers/gpu/drm/armada/armada_debugfs.c183
-rw-r--r--drivers/gpu/drm/armada/armada_drm.h112
-rw-r--r--drivers/gpu/drm/armada/armada_drv.c380
-rw-r--r--drivers/gpu/drm/armada/armada_fb.c170
-rw-r--r--drivers/gpu/drm/armada/armada_fb.h24
-rw-r--r--drivers/gpu/drm/armada/armada_fbdev.c202
-rw-r--r--drivers/gpu/drm/armada/armada_gem.c611
-rw-r--r--drivers/gpu/drm/armada/armada_gem.h52
-rw-r--r--drivers/gpu/drm/armada/armada_hw.h316
-rw-r--r--drivers/gpu/drm/armada/armada_ioctlP.h18
-rw-r--r--drivers/gpu/drm/armada/armada_output.c158
-rw-r--r--drivers/gpu/drm/armada/armada_output.h39
-rw-r--r--drivers/gpu/drm/armada/armada_overlay.c477
-rw-r--r--drivers/gpu/drm/armada/armada_slave.c139
-rw-r--r--drivers/gpu/drm/armada/armada_slave.h26
-rw-r--r--include/drm/drm_crtc.h17
-rw-r--r--include/uapi/drm/armada_drm.h45
24 files changed, 4015 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 955555d6ec88..3b5176dd1c86 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -225,6 +225,8 @@ source "drivers/gpu/drm/mgag200/Kconfig"
225 225
226source "drivers/gpu/drm/cirrus/Kconfig" 226source "drivers/gpu/drm/cirrus/Kconfig"
227 227
228source "drivers/gpu/drm/armada/Kconfig"
229
228source "drivers/gpu/drm/rcar-du/Kconfig" 230source "drivers/gpu/drm/rcar-du/Kconfig"
229 231
230source "drivers/gpu/drm/shmobile/Kconfig" 232source "drivers/gpu/drm/shmobile/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index f089adfe70ee..385f460459d4 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
49obj-$(CONFIG_DRM_GMA500) += gma500/ 49obj-$(CONFIG_DRM_GMA500) += gma500/
50obj-$(CONFIG_DRM_UDL) += udl/ 50obj-$(CONFIG_DRM_UDL) += udl/
51obj-$(CONFIG_DRM_AST) += ast/ 51obj-$(CONFIG_DRM_AST) += ast/
52obj-$(CONFIG_DRM_ARMADA) += armada/
52obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ 53obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
53obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ 54obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
54obj-$(CONFIG_DRM_OMAP) += omapdrm/ 55obj-$(CONFIG_DRM_OMAP) += omapdrm/
diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig
new file mode 100644
index 000000000000..c7a0a944acfe
--- /dev/null
+++ b/drivers/gpu/drm/armada/Kconfig
@@ -0,0 +1,15 @@
1config DRM_ARMADA
2 tristate "DRM support for Marvell Armada SoCs"
3 depends on DRM && HAVE_CLK
4 select FB_CFB_FILLRECT
5 select FB_CFB_COPYAREA
6 select FB_CFB_IMAGEBLIT
7 select DRM_KMS_HELPER
8 help
9 Support the "LCD" controllers found on the Marvell Armada 510
10 devices. There are two controllers on the device, each controller
11 supports graphics and video overlays.
12
13 This driver provides no built-in acceleration; acceleration is
14 performed by other IP found on the SoC. This driver provides
15 kernel mode setting and buffer management to userspace.
diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile
new file mode 100644
index 000000000000..d6f43e06150a
--- /dev/null
+++ b/drivers/gpu/drm/armada/Makefile
@@ -0,0 +1,7 @@
1armada-y := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
2 armada_gem.o armada_output.o armada_overlay.o \
3 armada_slave.o
4armada-y += armada_510.o
5armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
6
7obj-$(CONFIG_DRM_ARMADA) := armada.o
diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c
new file mode 100644
index 000000000000..a016888acd29
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_510.c
@@ -0,0 +1,86 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * Armada 510 (aka Dove) variant support
9 */
10#include <linux/clk.h>
11#include <linux/io.h>
12#include <drm/drmP.h>
13#include <drm/drm_crtc_helper.h>
14#include "armada_crtc.h"
15#include "armada_drm.h"
16#include "armada_hw.h"
17
18static int armada510_init(struct armada_private *priv, struct device *dev)
19{
20 priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1");
21
22 if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
23 priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
24
25 return PTR_RET(priv->extclk[0]);
26}
27
28static int armada510_crtc_init(struct armada_crtc *dcrtc)
29{
30 /* Lower the watermark so to eliminate jitter at higher bandwidths */
31 armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
32 return 0;
33}
34
35/*
36 * Armada510 specific SCLK register selection.
37 * This gets called with sclk = NULL to test whether the mode is
38 * supportable, and again with sclk != NULL to set the clocks up for
39 * that. The former can return an error, but the latter is expected
40 * not to.
41 *
42 * We currently are pretty rudimentary here, always selecting
43 * EXT_REF_CLK_1 for LCD0 and erroring LCD1. This needs improvement!
44 */
45static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
46 const struct drm_display_mode *mode, uint32_t *sclk)
47{
48 struct armada_private *priv = dcrtc->crtc.dev->dev_private;
49 struct clk *clk = priv->extclk[0];
50 int ret;
51
52 if (dcrtc->num == 1)
53 return -EINVAL;
54
55 if (IS_ERR(clk))
56 return PTR_ERR(clk);
57
58 if (dcrtc->clk != clk) {
59 ret = clk_prepare_enable(clk);
60 if (ret)
61 return ret;
62 dcrtc->clk = clk;
63 }
64
65 if (sclk) {
66 uint32_t rate, ref, div;
67
68 rate = mode->clock * 1000;
69 ref = clk_round_rate(clk, rate);
70 div = DIV_ROUND_UP(ref, rate);
71 if (div < 1)
72 div = 1;
73
74 clk_set_rate(clk, ref);
75 *sclk = div | SCLK_510_EXTCLK1;
76 }
77
78 return 0;
79}
80
81const struct armada_variant armada510_ops = {
82 .has_spu_adv_reg = true,
83 .init = armada510_init,
84 .crtc_init = armada510_crtc_init,
85 .crtc_compute_clock = armada510_crtc_compute_clock,
86};
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
new file mode 100644
index 000000000000..7b379fdb4e8c
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -0,0 +1,861 @@
1/*
2 * Copyright (C) 2012 Russell King
3 * Rewritten from the dovefb driver, and Armada510 manuals.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9#include <linux/clk.h>
10#include <drm/drmP.h>
11#include <drm/drm_crtc_helper.h>
12#include "armada_crtc.h"
13#include "armada_drm.h"
14#include "armada_fb.h"
15#include "armada_gem.h"
16#include "armada_hw.h"
17
18struct armada_frame_work {
19 struct drm_pending_vblank_event *event;
20 struct armada_regs regs[4];
21 struct drm_framebuffer *old_fb;
22};
23
24enum csc_mode {
25 CSC_AUTO = 0,
26 CSC_YUV_CCIR601 = 1,
27 CSC_YUV_CCIR709 = 2,
28 CSC_RGB_COMPUTER = 1,
29 CSC_RGB_STUDIO = 2,
30};
31
32/*
33 * A note about interlacing. Let's consider HDMI 1920x1080i.
34 * The timing parameters we have from X are:
35 * Hact HsyA HsyI Htot Vact VsyA VsyI Vtot
36 * 1920 2448 2492 2640 1080 1084 1094 1125
37 * Which get translated to:
38 * Hact HsyA HsyI Htot Vact VsyA VsyI Vtot
39 * 1920 2448 2492 2640 540 542 547 562
40 *
41 * This is how it is defined by CEA-861-D - line and pixel numbers are
42 * referenced to the rising edge of VSYNC and HSYNC. Total clocks per
43 * line: 2640. The odd frame, the first active line is at line 21, and
44 * the even frame, the first active line is 584.
45 *
46 * LN: 560 561 562 563 567 568 569
47 * DE: ~~~|____________________________//__________________________
48 * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____
49 * VSYNC: _________________________|~~~~~~//~~~~~~~~~~~~~~~|__________
50 * 22 blanking lines. VSYNC at 1320 (referenced to the HSYNC rising edge).
51 *
52 * LN: 1123 1124 1125 1 5 6 7
53 * DE: ~~~|____________________________//__________________________
54 * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____
55 * VSYNC: ____________________|~~~~~~~~~~~//~~~~~~~~~~|_______________
56 * 23 blanking lines
57 *
58 * The Armada LCD Controller line and pixel numbers are, like X timings,
59 * referenced to the top left of the active frame.
60 *
61 * So, translating these to our LCD controller:
62 * Odd frame, 563 total lines, VSYNC at line 543-548, pixel 1128.
63 * Even frame, 562 total lines, VSYNC at line 542-547, pixel 2448.
64 * Note: Vsync front porch remains constant!
65 *
66 * if (odd_frame) {
67 * vtotal = mode->crtc_vtotal + 1;
68 * vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay + 1;
69 * vhorizpos = mode->crtc_hsync_start - mode->crtc_htotal / 2
70 * } else {
71 * vtotal = mode->crtc_vtotal;
72 * vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay;
73 * vhorizpos = mode->crtc_hsync_start;
74 * }
75 * vfrontporch = mode->crtc_vtotal - mode->crtc_vsync_end;
76 *
77 * So, we need to reprogram these registers on each vsync event:
78 * LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL
79 *
80 * Note: we do not use the frame done interrupts because these appear
81 * to happen too early, and lead to jitter on the display (presumably
82 * they occur at the end of the last active line, before the vsync back
83 * porch, which we're reprogramming.)
84 */
85
86void
87armada_drm_crtc_update_regs(struct armada_crtc *dcrtc, struct armada_regs *regs)
88{
89 while (regs->offset != ~0) {
90 void __iomem *reg = dcrtc->base + regs->offset;
91 uint32_t val;
92
93 val = regs->mask;
94 if (val != 0)
95 val &= readl_relaxed(reg);
96 writel_relaxed(val | regs->val, reg);
97 ++regs;
98 }
99}
100
101#define dpms_blanked(dpms) ((dpms) != DRM_MODE_DPMS_ON)
102
103static void armada_drm_crtc_update(struct armada_crtc *dcrtc)
104{
105 uint32_t dumb_ctrl;
106
107 dumb_ctrl = dcrtc->cfg_dumb_ctrl;
108
109 if (!dpms_blanked(dcrtc->dpms))
110 dumb_ctrl |= CFG_DUMB_ENA;
111
112 /*
113 * When the dumb interface isn't in DUMB24_RGB888_0 mode, it might
114 * be using SPI or GPIO. If we set this to DUMB_BLANK, we will
115 * force LCD_D[23:0] to output blank color, overriding the GPIO or
116 * SPI usage. So leave it as-is unless in DUMB24_RGB888_0 mode.
117 */
118 if (dpms_blanked(dcrtc->dpms) &&
119 (dumb_ctrl & DUMB_MASK) == DUMB24_RGB888_0) {
120 dumb_ctrl &= ~DUMB_MASK;
121 dumb_ctrl |= DUMB_BLANK;
122 }
123
124 /*
125 * The documentation doesn't indicate what the normal state of
126 * the sync signals are. Sebastian Hesselbart kindly probed
127 * these signals on his board to determine their state.
128 *
129 * The non-inverted state of the sync signals is active high.
130 * Setting these bits makes the appropriate signal active low.
131 */
132 if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NCSYNC)
133 dumb_ctrl |= CFG_INV_CSYNC;
134 if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NHSYNC)
135 dumb_ctrl |= CFG_INV_HSYNC;
136 if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NVSYNC)
137 dumb_ctrl |= CFG_INV_VSYNC;
138
139 if (dcrtc->dumb_ctrl != dumb_ctrl) {
140 dcrtc->dumb_ctrl = dumb_ctrl;
141 writel_relaxed(dumb_ctrl, dcrtc->base + LCD_SPU_DUMB_CTRL);
142 }
143}
144
145static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
146 int x, int y, struct armada_regs *regs, bool interlaced)
147{
148 struct armada_gem_object *obj = drm_fb_obj(fb);
149 unsigned pitch = fb->pitches[0];
150 unsigned offset = y * pitch + x * fb->bits_per_pixel / 8;
151 uint32_t addr_odd, addr_even;
152 unsigned i = 0;
153
154 DRM_DEBUG_DRIVER("pitch %u x %d y %d bpp %d\n",
155 pitch, x, y, fb->bits_per_pixel);
156
157 addr_odd = addr_even = obj->dev_addr + offset;
158
159 if (interlaced) {
160 addr_even += pitch;
161 pitch *= 2;
162 }
163
164 /* write offset, base, and pitch */
165 armada_reg_queue_set(regs, i, addr_odd, LCD_CFG_GRA_START_ADDR0);
166 armada_reg_queue_set(regs, i, addr_even, LCD_CFG_GRA_START_ADDR1);
167 armada_reg_queue_mod(regs, i, pitch, 0xffff, LCD_CFG_GRA_PITCH);
168
169 return i;
170}
171
172static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
173 struct armada_frame_work *work)
174{
175 struct drm_device *dev = dcrtc->crtc.dev;
176 unsigned long flags;
177 int ret;
178
179 ret = drm_vblank_get(dev, dcrtc->num);
180 if (ret) {
181 DRM_ERROR("failed to acquire vblank counter\n");
182 return ret;
183 }
184
185 spin_lock_irqsave(&dev->event_lock, flags);
186 if (!dcrtc->frame_work)
187 dcrtc->frame_work = work;
188 else
189 ret = -EBUSY;
190 spin_unlock_irqrestore(&dev->event_lock, flags);
191
192 if (ret)
193 drm_vblank_put(dev, dcrtc->num);
194
195 return ret;
196}
197
198static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc)
199{
200 struct drm_device *dev = dcrtc->crtc.dev;
201 struct armada_frame_work *work = dcrtc->frame_work;
202
203 dcrtc->frame_work = NULL;
204
205 armada_drm_crtc_update_regs(dcrtc, work->regs);
206
207 if (work->event)
208 drm_send_vblank_event(dev, dcrtc->num, work->event);
209
210 drm_vblank_put(dev, dcrtc->num);
211
212 /* Finally, queue the process-half of the cleanup. */
213 __armada_drm_queue_unref_work(dcrtc->crtc.dev, work->old_fb);
214 kfree(work);
215}
216
217static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
218 struct drm_framebuffer *fb, bool force)
219{
220 struct armada_frame_work *work;
221
222 if (!fb)
223 return;
224
225 if (force) {
226 /* Display is disabled, so just drop the old fb */
227 drm_framebuffer_unreference(fb);
228 return;
229 }
230
231 work = kmalloc(sizeof(*work), GFP_KERNEL);
232 if (work) {
233 int i = 0;
234 work->event = NULL;
235 work->old_fb = fb;
236 armada_reg_queue_end(work->regs, i);
237
238 if (armada_drm_crtc_queue_frame_work(dcrtc, work) == 0)
239 return;
240
241 kfree(work);
242 }
243
244 /*
245 * Oops - just drop the reference immediately and hope for
246 * the best. The worst that will happen is the buffer gets
247 * reused before it has finished being displayed.
248 */
249 drm_framebuffer_unreference(fb);
250}
251
252static void armada_drm_vblank_off(struct armada_crtc *dcrtc)
253{
254 struct drm_device *dev = dcrtc->crtc.dev;
255
256 /*
257 * Tell the DRM core that vblank IRQs aren't going to happen for
258 * a while. This cleans up any pending vblank events for us.
259 */
260 drm_vblank_off(dev, dcrtc->num);
261
262 /* Handle any pending flip event. */
263 spin_lock_irq(&dev->event_lock);
264 if (dcrtc->frame_work)
265 armada_drm_crtc_complete_frame_work(dcrtc);
266 spin_unlock_irq(&dev->event_lock);
267}
268
269void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b,
270 int idx)
271{
272}
273
274void armada_drm_crtc_gamma_get(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
275 int idx)
276{
277}
278
279/* The mode_config.mutex will be held for this call */
280static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms)
281{
282 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
283
284 if (dcrtc->dpms != dpms) {
285 dcrtc->dpms = dpms;
286 armada_drm_crtc_update(dcrtc);
287 if (dpms_blanked(dpms))
288 armada_drm_vblank_off(dcrtc);
289 }
290}
291
292/*
293 * Prepare for a mode set. Turn off overlay to ensure that we don't end
294 * up with the overlay size being bigger than the active screen size.
295 * We rely upon X refreshing this state after the mode set has completed.
296 *
297 * The mode_config.mutex will be held for this call
298 */
299static void armada_drm_crtc_prepare(struct drm_crtc *crtc)
300{
301 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
302 struct drm_plane *plane;
303
304 /*
305 * If we have an overlay plane associated with this CRTC, disable
306 * it before the modeset to avoid its coordinates being outside
307 * the new mode parameters. DRM doesn't provide help with this.
308 */
309 plane = dcrtc->plane;
310 if (plane) {
311 struct drm_framebuffer *fb = plane->fb;
312
313 plane->funcs->disable_plane(plane);
314 plane->fb = NULL;
315 plane->crtc = NULL;
316 drm_framebuffer_unreference(fb);
317 }
318}
319
320/* The mode_config.mutex will be held for this call */
321static void armada_drm_crtc_commit(struct drm_crtc *crtc)
322{
323 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
324
325 if (dcrtc->dpms != DRM_MODE_DPMS_ON) {
326 dcrtc->dpms = DRM_MODE_DPMS_ON;
327 armada_drm_crtc_update(dcrtc);
328 }
329}
330
331/* The mode_config.mutex will be held for this call */
332static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
333 const struct drm_display_mode *mode, struct drm_display_mode *adj)
334{
335 struct armada_private *priv = crtc->dev->dev_private;
336 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
337 int ret;
338
339 /* We can't do interlaced modes if we don't have the SPU_ADV_REG */
340 if (!priv->variant->has_spu_adv_reg &&
341 adj->flags & DRM_MODE_FLAG_INTERLACE)
342 return false;
343
344 /* Check whether the display mode is possible */
345 ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL);
346 if (ret)
347 return false;
348
349 return true;
350}
351
352void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
353{
354 struct armada_vbl_event *e, *n;
355 void __iomem *base = dcrtc->base;
356
357 if (stat & DMA_FF_UNDERFLOW)
358 DRM_ERROR("video underflow on crtc %u\n", dcrtc->num);
359 if (stat & GRA_FF_UNDERFLOW)
360 DRM_ERROR("graphics underflow on crtc %u\n", dcrtc->num);
361
362 if (stat & VSYNC_IRQ)
363 drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num);
364
365 spin_lock(&dcrtc->irq_lock);
366
367 list_for_each_entry_safe(e, n, &dcrtc->vbl_list, node) {
368 list_del_init(&e->node);
369 drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
370 e->fn(dcrtc, e->data);
371 }
372
373 if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
374 int i = stat & GRA_FRAME_IRQ0 ? 0 : 1;
375 uint32_t val;
376
377 writel_relaxed(dcrtc->v[i].spu_v_porch, base + LCD_SPU_V_PORCH);
378 writel_relaxed(dcrtc->v[i].spu_v_h_total,
379 base + LCD_SPUT_V_H_TOTAL);
380
381 val = readl_relaxed(base + LCD_SPU_ADV_REG);
382 val &= ~(ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN);
383 val |= dcrtc->v[i].spu_adv_reg;
384 writel_relaxed(val, dcrtc->base + LCD_SPU_ADV_REG);
385 }
386 spin_unlock(&dcrtc->irq_lock);
387
388 if (stat & GRA_FRAME_IRQ) {
389 struct drm_device *dev = dcrtc->crtc.dev;
390
391 spin_lock(&dev->event_lock);
392 if (dcrtc->frame_work)
393 armada_drm_crtc_complete_frame_work(dcrtc);
394 spin_unlock(&dev->event_lock);
395
396 wake_up(&dcrtc->frame_wait);
397 }
398}
399
400/* These are locked by dev->vbl_lock */
401void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
402{
403 if (dcrtc->irq_ena & mask) {
404 dcrtc->irq_ena &= ~mask;
405 writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
406 }
407}
408
409void armada_drm_crtc_enable_irq(struct armada_crtc *dcrtc, u32 mask)
410{
411 if ((dcrtc->irq_ena & mask) != mask) {
412 dcrtc->irq_ena |= mask;
413 writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
414 if (readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR) & mask)
415 writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
416 }
417}
418
419static uint32_t armada_drm_crtc_calculate_csc(struct armada_crtc *dcrtc)
420{
421 struct drm_display_mode *adj = &dcrtc->crtc.mode;
422 uint32_t val = 0;
423
424 if (dcrtc->csc_yuv_mode == CSC_YUV_CCIR709)
425 val |= CFG_CSC_YUV_CCIR709;
426 if (dcrtc->csc_rgb_mode == CSC_RGB_STUDIO)
427 val |= CFG_CSC_RGB_STUDIO;
428
429 /*
430 * In auto mode, set the colorimetry, based upon the HDMI spec.
431 * 1280x720p, 1920x1080p and 1920x1080i use ITU709, others use
432 * ITU601. It may be more appropriate to set this depending on
433 * the source - but what if the graphic frame is YUV and the
434 * video frame is RGB?
435 */
436 if ((adj->hdisplay == 1280 && adj->vdisplay == 720 &&
437 !(adj->flags & DRM_MODE_FLAG_INTERLACE)) ||
438 (adj->hdisplay == 1920 && adj->vdisplay == 1080)) {
439 if (dcrtc->csc_yuv_mode == CSC_AUTO)
440 val |= CFG_CSC_YUV_CCIR709;
441 }
442
443 /*
444 * We assume we're connected to a TV-like device, so the YUV->RGB
445 * conversion should produce a limited range. We should set this
446 * depending on the connectors attached to this CRTC, and what
447 * kind of device they report being connected.
448 */
449 if (dcrtc->csc_rgb_mode == CSC_AUTO)
450 val |= CFG_CSC_RGB_STUDIO;
451
452 return val;
453}
454
455/* The mode_config.mutex will be held for this call */
456static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
457 struct drm_display_mode *mode, struct drm_display_mode *adj,
458 int x, int y, struct drm_framebuffer *old_fb)
459{
460 struct armada_private *priv = crtc->dev->dev_private;
461 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
462 struct armada_regs regs[17];
463 uint32_t lm, rm, tm, bm, val, sclk;
464 unsigned long flags;
465 unsigned i;
466 bool interlaced;
467
468 drm_framebuffer_reference(crtc->fb);
469
470 interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE);
471
472 i = armada_drm_crtc_calc_fb(dcrtc->crtc.fb, x, y, regs, interlaced);
473
474 rm = adj->crtc_hsync_start - adj->crtc_hdisplay;
475 lm = adj->crtc_htotal - adj->crtc_hsync_end;
476 bm = adj->crtc_vsync_start - adj->crtc_vdisplay;
477 tm = adj->crtc_vtotal - adj->crtc_vsync_end;
478
479 DRM_DEBUG_DRIVER("H: %d %d %d %d lm %d rm %d\n",
480 adj->crtc_hdisplay,
481 adj->crtc_hsync_start,
482 adj->crtc_hsync_end,
483 adj->crtc_htotal, lm, rm);
484 DRM_DEBUG_DRIVER("V: %d %d %d %d tm %d bm %d\n",
485 adj->crtc_vdisplay,
486 adj->crtc_vsync_start,
487 adj->crtc_vsync_end,
488 adj->crtc_vtotal, tm, bm);
489
490 /* Wait for pending flips to complete */
491 wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
492
493 drm_vblank_pre_modeset(crtc->dev, dcrtc->num);
494
495 crtc->mode = *adj;
496
497 val = dcrtc->dumb_ctrl & ~CFG_DUMB_ENA;
498 if (val != dcrtc->dumb_ctrl) {
499 dcrtc->dumb_ctrl = val;
500 writel_relaxed(val, dcrtc->base + LCD_SPU_DUMB_CTRL);
501 }
502
503 /* Now compute the divider for real */
504 priv->variant->crtc_compute_clock(dcrtc, adj, &sclk);
505
506 /* Ensure graphic fifo is enabled */
507 armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
508 armada_reg_queue_set(regs, i, sclk, LCD_CFG_SCLK_DIV);
509
510 if (interlaced ^ dcrtc->interlaced) {
511 if (adj->flags & DRM_MODE_FLAG_INTERLACE)
512 drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
513 else
514 drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
515 dcrtc->interlaced = interlaced;
516 }
517
518 spin_lock_irqsave(&dcrtc->irq_lock, flags);
519
520 /* Even interlaced/progressive frame */
521 dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 |
522 adj->crtc_htotal;
523 dcrtc->v[1].spu_v_porch = tm << 16 | bm;
524 val = adj->crtc_hsync_start;
525 dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN;
526
527 if (interlaced) {
528 /* Odd interlaced frame */
529 dcrtc->v[0].spu_v_h_total = dcrtc->v[1].spu_v_h_total +
530 (1 << 16);
531 dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1;
532 val = adj->crtc_hsync_start - adj->crtc_htotal / 2;
533 dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN;
534 } else {
535 dcrtc->v[0] = dcrtc->v[1];
536 }
537
538 val = adj->crtc_vdisplay << 16 | adj->crtc_hdisplay;
539
540 armada_reg_queue_set(regs, i, val, LCD_SPU_V_H_ACTIVE);
541 armada_reg_queue_set(regs, i, val, LCD_SPU_GRA_HPXL_VLN);
542 armada_reg_queue_set(regs, i, val, LCD_SPU_GZM_HPXL_VLN);
543 armada_reg_queue_set(regs, i, (lm << 16) | rm, LCD_SPU_H_PORCH);
544 armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_porch, LCD_SPU_V_PORCH);
545 armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
546 LCD_SPUT_V_H_TOTAL);
547
548 if (priv->variant->has_spu_adv_reg)
549 armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
550 ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
551 ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
552
553 val = CFG_GRA_ENA | CFG_GRA_HSMOOTH;
554 val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt);
555 val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.fb)->mod);
556
557 if (drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt > CFG_420)
558 val |= CFG_PALETTE_ENA;
559
560 if (interlaced)
561 val |= CFG_GRA_FTOGGLE;
562
563 armada_reg_queue_mod(regs, i, val, CFG_GRAFORMAT |
564 CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
565 CFG_SWAPYU | CFG_YUV2RGB) |
566 CFG_PALETTE_ENA | CFG_GRA_FTOGGLE,
567 LCD_SPU_DMA_CTRL0);
568
569 val = adj->flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0;
570 armada_reg_queue_mod(regs, i, val, CFG_VSYNC_INV, LCD_SPU_DMA_CTRL1);
571
572 val = dcrtc->spu_iopad_ctrl | armada_drm_crtc_calculate_csc(dcrtc);
573 armada_reg_queue_set(regs, i, val, LCD_SPU_IOPAD_CONTROL);
574 armada_reg_queue_end(regs, i);
575
576 armada_drm_crtc_update_regs(dcrtc, regs);
577 spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
578
579 armada_drm_crtc_update(dcrtc);
580
581 drm_vblank_post_modeset(crtc->dev, dcrtc->num);
582 armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
583
584 return 0;
585}
586
587/* The mode_config.mutex will be held for this call */
588static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
589 struct drm_framebuffer *old_fb)
590{
591 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
592 struct armada_regs regs[4];
593 unsigned i;
594
595 i = armada_drm_crtc_calc_fb(crtc->fb, crtc->x, crtc->y, regs,
596 dcrtc->interlaced);
597 armada_reg_queue_end(regs, i);
598
599 /* Wait for pending flips to complete */
600 wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
601
602 /* Take a reference to the new fb as we're using it */
603 drm_framebuffer_reference(crtc->fb);
604
605 /* Update the base in the CRTC */
606 armada_drm_crtc_update_regs(dcrtc, regs);
607
608 /* Drop our previously held reference */
609 armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
610
611 return 0;
612}
613
614static void armada_drm_crtc_load_lut(struct drm_crtc *crtc)
615{
616}
617
618/* The mode_config.mutex will be held for this call */
619static void armada_drm_crtc_disable(struct drm_crtc *crtc)
620{
621 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
622
623 armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
624 armada_drm_crtc_finish_fb(dcrtc, crtc->fb, true);
625
626 /* Power down most RAMs and FIFOs */
627 writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
628 CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
629 CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
630}
631
632static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
633 .dpms = armada_drm_crtc_dpms,
634 .prepare = armada_drm_crtc_prepare,
635 .commit = armada_drm_crtc_commit,
636 .mode_fixup = armada_drm_crtc_mode_fixup,
637 .mode_set = armada_drm_crtc_mode_set,
638 .mode_set_base = armada_drm_crtc_mode_set_base,
639 .load_lut = armada_drm_crtc_load_lut,
640 .disable = armada_drm_crtc_disable,
641};
642
643static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
644{
645 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
646 struct armada_private *priv = crtc->dev->dev_private;
647
648 priv->dcrtc[dcrtc->num] = NULL;
649 drm_crtc_cleanup(&dcrtc->crtc);
650
651 if (!IS_ERR(dcrtc->clk))
652 clk_disable_unprepare(dcrtc->clk);
653
654 kfree(dcrtc);
655}
656
657/*
658 * The mode_config lock is held here, to prevent races between this
659 * and a mode_set.
660 */
661static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
662 struct drm_framebuffer *fb, struct drm_pending_vblank_event *event)
663{
664 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
665 struct armada_frame_work *work;
666 struct drm_device *dev = crtc->dev;
667 unsigned long flags;
668 unsigned i;
669 int ret;
670
671 /* We don't support changing the pixel format */
672 if (fb->pixel_format != crtc->fb->pixel_format)
673 return -EINVAL;
674
675 work = kmalloc(sizeof(*work), GFP_KERNEL);
676 if (!work)
677 return -ENOMEM;
678
679 work->event = event;
680 work->old_fb = dcrtc->crtc.fb;
681
682 i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs,
683 dcrtc->interlaced);
684 armada_reg_queue_end(work->regs, i);
685
686 /*
687 * Hold the old framebuffer for the work - DRM appears to drop our
688 * reference to the old framebuffer in drm_mode_page_flip_ioctl().
689 */
690 drm_framebuffer_reference(work->old_fb);
691
692 ret = armada_drm_crtc_queue_frame_work(dcrtc, work);
693 if (ret) {
694 /*
695 * Undo our reference above; DRM does not drop the reference
696 * to this object on error, so that's okay.
697 */
698 drm_framebuffer_unreference(work->old_fb);
699 kfree(work);
700 return ret;
701 }
702
703 /*
704 * Don't take a reference on the new framebuffer;
705 * drm_mode_page_flip_ioctl() has already grabbed a reference and
706 * will _not_ drop that reference on successful return from this
707 * function. Simply mark this new framebuffer as the current one.
708 */
709 dcrtc->crtc.fb = fb;
710
711 /*
712 * Finally, if the display is blanked, we won't receive an
713 * interrupt, so complete it now.
714 */
715 if (dpms_blanked(dcrtc->dpms)) {
716 spin_lock_irqsave(&dev->event_lock, flags);
717 if (dcrtc->frame_work)
718 armada_drm_crtc_complete_frame_work(dcrtc);
719 spin_unlock_irqrestore(&dev->event_lock, flags);
720 }
721
722 return 0;
723}
724
725static int
726armada_drm_crtc_set_property(struct drm_crtc *crtc,
727 struct drm_property *property, uint64_t val)
728{
729 struct armada_private *priv = crtc->dev->dev_private;
730 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
731 bool update_csc = false;
732
733 if (property == priv->csc_yuv_prop) {
734 dcrtc->csc_yuv_mode = val;
735 update_csc = true;
736 } else if (property == priv->csc_rgb_prop) {
737 dcrtc->csc_rgb_mode = val;
738 update_csc = true;
739 }
740
741 if (update_csc) {
742 uint32_t val;
743
744 val = dcrtc->spu_iopad_ctrl |
745 armada_drm_crtc_calculate_csc(dcrtc);
746 writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
747 }
748
749 return 0;
750}
751
752static struct drm_crtc_funcs armada_crtc_funcs = {
753 .destroy = armada_drm_crtc_destroy,
754 .set_config = drm_crtc_helper_set_config,
755 .page_flip = armada_drm_crtc_page_flip,
756 .set_property = armada_drm_crtc_set_property,
757};
758
759static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = {
760 { CSC_AUTO, "Auto" },
761 { CSC_YUV_CCIR601, "CCIR601" },
762 { CSC_YUV_CCIR709, "CCIR709" },
763};
764
765static struct drm_prop_enum_list armada_drm_csc_rgb_enum_list[] = {
766 { CSC_AUTO, "Auto" },
767 { CSC_RGB_COMPUTER, "Computer system" },
768 { CSC_RGB_STUDIO, "Studio" },
769};
770
771static int armada_drm_crtc_create_properties(struct drm_device *dev)
772{
773 struct armada_private *priv = dev->dev_private;
774
775 if (priv->csc_yuv_prop)
776 return 0;
777
778 priv->csc_yuv_prop = drm_property_create_enum(dev, 0,
779 "CSC_YUV", armada_drm_csc_yuv_enum_list,
780 ARRAY_SIZE(armada_drm_csc_yuv_enum_list));
781 priv->csc_rgb_prop = drm_property_create_enum(dev, 0,
782 "CSC_RGB", armada_drm_csc_rgb_enum_list,
783 ARRAY_SIZE(armada_drm_csc_rgb_enum_list));
784
785 if (!priv->csc_yuv_prop || !priv->csc_rgb_prop)
786 return -ENOMEM;
787
788 return 0;
789}
790
791int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
792 struct resource *res)
793{
794 struct armada_private *priv = dev->dev_private;
795 struct armada_crtc *dcrtc;
796 void __iomem *base;
797 int ret;
798
799 ret = armada_drm_crtc_create_properties(dev);
800 if (ret)
801 return ret;
802
803 base = devm_request_and_ioremap(dev->dev, res);
804 if (!base) {
805 DRM_ERROR("failed to ioremap register\n");
806 return -ENOMEM;
807 }
808
809 dcrtc = kzalloc(sizeof(*dcrtc), GFP_KERNEL);
810 if (!dcrtc) {
811 DRM_ERROR("failed to allocate Armada crtc\n");
812 return -ENOMEM;
813 }
814
815 dcrtc->base = base;
816 dcrtc->num = num;
817 dcrtc->clk = ERR_PTR(-EINVAL);
818 dcrtc->csc_yuv_mode = CSC_AUTO;
819 dcrtc->csc_rgb_mode = CSC_AUTO;
820 dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
821 dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
822 spin_lock_init(&dcrtc->irq_lock);
823 dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR;
824 INIT_LIST_HEAD(&dcrtc->vbl_list);
825 init_waitqueue_head(&dcrtc->frame_wait);
826
827 /* Initialize some registers which we don't otherwise set */
828 writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV);
829 writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_BLANKCOLOR);
830 writel_relaxed(dcrtc->spu_iopad_ctrl,
831 dcrtc->base + LCD_SPU_IOPAD_CONTROL);
832 writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_SRAM_PARA0);
833 writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
834 CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
835 CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
836 writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1);
837 writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN);
838
839 if (priv->variant->crtc_init) {
840 ret = priv->variant->crtc_init(dcrtc);
841 if (ret) {
842 kfree(dcrtc);
843 return ret;
844 }
845 }
846
847 /* Ensure AXI pipeline is enabled */
848 armada_updatel(CFG_ARBFAST_ENA, 0, dcrtc->base + LCD_SPU_DMA_CTRL0);
849
850 priv->dcrtc[dcrtc->num] = dcrtc;
851
852 drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs);
853 drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
854
855 drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
856 dcrtc->csc_yuv_mode);
857 drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop,
858 dcrtc->csc_rgb_mode);
859
860 return armada_overlay_plane_create(dev, 1 << dcrtc->num);
861}
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
new file mode 100644
index 000000000000..972da535921d
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -0,0 +1,74 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#ifndef ARMADA_CRTC_H
9#define ARMADA_CRTC_H
10
11struct armada_gem_object;
12
13struct armada_regs {
14 uint32_t offset;
15 uint32_t mask;
16 uint32_t val;
17};
18
19#define armada_reg_queue_mod(_r, _i, _v, _m, _o) \
20 do { \
21 struct armada_regs *__reg = _r; \
22 __reg[_i].offset = _o; \
23 __reg[_i].mask = ~(_m); \
24 __reg[_i].val = _v; \
25 _i++; \
26 } while (0)
27
28#define armada_reg_queue_set(_r, _i, _v, _o) \
29 armada_reg_queue_mod(_r, _i, _v, ~0, _o)
30
31#define armada_reg_queue_end(_r, _i) \
32 armada_reg_queue_mod(_r, _i, 0, 0, ~0)
33
34struct armada_frame_work;
35
36struct armada_crtc {
37 struct drm_crtc crtc;
38 unsigned num;
39 void __iomem *base;
40 struct clk *clk;
41 struct {
42 uint32_t spu_v_h_total;
43 uint32_t spu_v_porch;
44 uint32_t spu_adv_reg;
45 } v[2];
46 bool interlaced;
47 uint8_t csc_yuv_mode;
48 uint8_t csc_rgb_mode;
49
50 struct drm_plane *plane;
51
52 int dpms;
53 uint32_t cfg_dumb_ctrl;
54 uint32_t dumb_ctrl;
55 uint32_t spu_iopad_ctrl;
56
57 wait_queue_head_t frame_wait;
58 struct armada_frame_work *frame_work;
59
60 spinlock_t irq_lock;
61 uint32_t irq_ena;
62 struct list_head vbl_list;
63};
64#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
65
66int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *);
67void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
68void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
69void armada_drm_crtc_irq(struct armada_crtc *, u32);
70void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
71void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
72void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
73
74#endif
diff --git a/drivers/gpu/drm/armada/armada_debugfs.c b/drivers/gpu/drm/armada/armada_debugfs.c
new file mode 100644
index 000000000000..612f3753cd92
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_debugfs.c
@@ -0,0 +1,183 @@
1/*
2 * Copyright (C) 2012 Russell King
3 * Rewritten from the dovefb driver, and Armada510 manuals.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9#include <linux/ctype.h>
10#include <linux/debugfs.h>
11#include <linux/module.h>
12#include <linux/seq_file.h>
13#include <drm/drmP.h>
14#include "armada_crtc.h"
15#include "armada_drm.h"
16
17static int armada_debugfs_gem_linear_show(struct seq_file *m, void *data)
18{
19 struct drm_info_node *node = m->private;
20 struct drm_device *dev = node->minor->dev;
21 struct armada_private *priv = dev->dev_private;
22 int ret;
23
24 mutex_lock(&dev->struct_mutex);
25 ret = drm_mm_dump_table(m, &priv->linear);
26 mutex_unlock(&dev->struct_mutex);
27
28 return ret;
29}
30
31static int armada_debugfs_reg_show(struct seq_file *m, void *data)
32{
33 struct drm_device *dev = m->private;
34 struct armada_private *priv = dev->dev_private;
35 int n, i;
36
37 if (priv) {
38 for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
39 struct armada_crtc *dcrtc = priv->dcrtc[n];
40 if (!dcrtc)
41 continue;
42
43 for (i = 0x84; i <= 0x1c4; i += 4) {
44 uint32_t v = readl_relaxed(dcrtc->base + i);
45 seq_printf(m, "%u: 0x%04x: 0x%08x\n", n, i, v);
46 }
47 }
48 }
49
50 return 0;
51}
52
53static int armada_debugfs_reg_r_open(struct inode *inode, struct file *file)
54{
55 return single_open(file, armada_debugfs_reg_show, inode->i_private);
56}
57
58static const struct file_operations fops_reg_r = {
59 .owner = THIS_MODULE,
60 .open = armada_debugfs_reg_r_open,
61 .read = seq_read,
62 .llseek = seq_lseek,
63 .release = single_release,
64};
65
66static int armada_debugfs_write(struct file *file, const char __user *ptr,
67 size_t len, loff_t *off)
68{
69 struct drm_device *dev = file->private_data;
70 struct armada_private *priv = dev->dev_private;
71 struct armada_crtc *dcrtc = priv->dcrtc[0];
72 char buf[32], *p;
73 uint32_t reg, val;
74 int ret;
75
76 if (*off != 0)
77 return 0;
78
79 if (len > sizeof(buf) - 1)
80 len = sizeof(buf) - 1;
81
82 ret = strncpy_from_user(buf, ptr, len);
83 if (ret < 0)
84 return ret;
85 buf[len] = '\0';
86
87 reg = simple_strtoul(buf, &p, 16);
88 if (!isspace(*p))
89 return -EINVAL;
90 val = simple_strtoul(p + 1, NULL, 16);
91
92 if (reg >= 0x84 && reg <= 0x1c4)
93 writel(val, dcrtc->base + reg);
94
95 return len;
96}
97
98static int armada_debugfs_reg_w_open(struct inode *inode, struct file *file)
99{
100 file->private_data = inode->i_private;
101 return 0;
102}
103
104static const struct file_operations fops_reg_w = {
105 .owner = THIS_MODULE,
106 .open = armada_debugfs_reg_w_open,
107 .write = armada_debugfs_write,
108 .llseek = noop_llseek,
109};
110
111static struct drm_info_list armada_debugfs_list[] = {
112 { "gem_linear", armada_debugfs_gem_linear_show, 0 },
113};
114#define ARMADA_DEBUGFS_ENTRIES ARRAY_SIZE(armada_debugfs_list)
115
116static int drm_add_fake_info_node(struct drm_minor *minor, struct dentry *ent,
117 const void *key)
118{
119 struct drm_info_node *node;
120
121 node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
122 if (node == NULL) {
123 debugfs_remove(ent);
124 return -ENOMEM;
125 }
126
127 node->minor = minor;
128 node->dent = ent;
129 node->info_ent = (void *) key;
130
131 mutex_lock(&minor->debugfs_lock);
132 list_add(&node->list, &minor->debugfs_list);
133 mutex_unlock(&minor->debugfs_lock);
134
135 return 0;
136}
137
138static int armada_debugfs_create(struct dentry *root, struct drm_minor *minor,
139 const char *name, umode_t mode, const struct file_operations *fops)
140{
141 struct dentry *de;
142
143 de = debugfs_create_file(name, mode, root, minor->dev, fops);
144
145 return drm_add_fake_info_node(minor, de, fops);
146}
147
148int armada_drm_debugfs_init(struct drm_minor *minor)
149{
150 int ret;
151
152 ret = drm_debugfs_create_files(armada_debugfs_list,
153 ARMADA_DEBUGFS_ENTRIES,
154 minor->debugfs_root, minor);
155 if (ret)
156 return ret;
157
158 ret = armada_debugfs_create(minor->debugfs_root, minor,
159 "reg", S_IFREG | S_IRUSR, &fops_reg_r);
160 if (ret)
161 goto err_1;
162
163 ret = armada_debugfs_create(minor->debugfs_root, minor,
164 "reg_wr", S_IFREG | S_IWUSR, &fops_reg_w);
165 if (ret)
166 goto err_2;
167 return ret;
168
169 err_2:
170 drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_r, 1, minor);
171 err_1:
172 drm_debugfs_remove_files(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
173 minor);
174 return ret;
175}
176
177void armada_drm_debugfs_cleanup(struct drm_minor *minor)
178{
179 drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_w, 1, minor);
180 drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_r, 1, minor);
181 drm_debugfs_remove_files(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
182 minor);
183}
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
new file mode 100644
index 000000000000..e8c4f80c61d4
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -0,0 +1,112 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#ifndef ARMADA_DRM_H
9#define ARMADA_DRM_H
10
11#include <linux/kfifo.h>
12#include <linux/io.h>
13#include <linux/workqueue.h>
14#include <drm/drmP.h>
15
16struct armada_crtc;
17struct armada_gem_object;
18struct clk;
19struct drm_fb_helper;
20
21static inline void
22armada_updatel(uint32_t val, uint32_t mask, void __iomem *ptr)
23{
24 uint32_t ov, v;
25
26 ov = v = readl_relaxed(ptr);
27 v = (v & ~mask) | val;
28 if (ov != v)
29 writel_relaxed(v, ptr);
30}
31
32static inline uint32_t armada_pitch(uint32_t width, uint32_t bpp)
33{
34 uint32_t pitch = bpp != 4 ? width * ((bpp + 7) / 8) : width / 2;
35
36 /* 88AP510 spec recommends pitch be a multiple of 128 */
37 return ALIGN(pitch, 128);
38}
39
40struct armada_vbl_event {
41 struct list_head node;
42 void *data;
43 void (*fn)(struct armada_crtc *, void *);
44};
45void armada_drm_vbl_event_add(struct armada_crtc *,
46 struct armada_vbl_event *);
47void armada_drm_vbl_event_remove(struct armada_crtc *,
48 struct armada_vbl_event *);
49void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *,
50 struct armada_vbl_event *);
51#define armada_drm_vbl_event_init(_e, _f, _d) do { \
52 struct armada_vbl_event *__e = _e; \
53 INIT_LIST_HEAD(&__e->node); \
54 __e->data = _d; \
55 __e->fn = _f; \
56} while (0)
57
58
59struct armada_private;
60
61struct armada_variant {
62 bool has_spu_adv_reg;
63 int (*init)(struct armada_private *, struct device *);
64 int (*crtc_init)(struct armada_crtc *);
65 int (*crtc_compute_clock)(struct armada_crtc *,
66 const struct drm_display_mode *,
67 uint32_t *);
68};
69
70/* Variant ops */
71extern const struct armada_variant armada510_ops;
72
73struct armada_private {
74 const struct armada_variant *variant;
75 struct work_struct fb_unref_work;
76 DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8);
77 struct drm_fb_helper *fbdev;
78 struct armada_crtc *dcrtc[2];
79 struct drm_mm linear;
80 struct clk *extclk[2];
81 struct drm_property *csc_yuv_prop;
82 struct drm_property *csc_rgb_prop;
83 struct drm_property *colorkey_prop;
84 struct drm_property *colorkey_min_prop;
85 struct drm_property *colorkey_max_prop;
86 struct drm_property *colorkey_val_prop;
87 struct drm_property *colorkey_alpha_prop;
88 struct drm_property *colorkey_mode_prop;
89 struct drm_property *brightness_prop;
90 struct drm_property *contrast_prop;
91 struct drm_property *saturation_prop;
92#ifdef CONFIG_DEBUG_FS
93 struct dentry *de;
94#endif
95};
96
97void __armada_drm_queue_unref_work(struct drm_device *,
98 struct drm_framebuffer *);
99void armada_drm_queue_unref_work(struct drm_device *,
100 struct drm_framebuffer *);
101
102extern const struct drm_mode_config_funcs armada_drm_mode_config_funcs;
103
104int armada_fbdev_init(struct drm_device *);
105void armada_fbdev_fini(struct drm_device *);
106
107int armada_overlay_plane_create(struct drm_device *, unsigned long);
108
109int armada_drm_debugfs_init(struct drm_minor *);
110void armada_drm_debugfs_cleanup(struct drm_minor *);
111
112#endif
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
new file mode 100644
index 000000000000..7bfab9a286ae
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -0,0 +1,380 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#include <linux/clk.h>
9#include <linux/module.h>
10#include <drm/drmP.h>
11#include <drm/drm_crtc_helper.h>
12#include "armada_crtc.h"
13#include "armada_drm.h"
14#include "armada_gem.h"
15#include "armada_hw.h"
16#include <drm/armada_drm.h>
17#include "armada_ioctlP.h"
18
19static void armada_drm_unref_work(struct work_struct *work)
20{
21 struct armada_private *priv =
22 container_of(work, struct armada_private, fb_unref_work);
23 struct drm_framebuffer *fb;
24
25 while (kfifo_get(&priv->fb_unref, &fb))
26 drm_framebuffer_unreference(fb);
27}
28
29/* Must be called with dev->event_lock held */
30void __armada_drm_queue_unref_work(struct drm_device *dev,
31 struct drm_framebuffer *fb)
32{
33 struct armada_private *priv = dev->dev_private;
34
35 /*
36 * Yes, we really must jump through these hoops just to store a
37 * _pointer_ to something into the kfifo. This is utterly insane
38 * and idiotic, because it kfifo requires the _data_ pointed to by
39 * the pointer const, not the pointer itself. Not only that, but
40 * you have to pass a pointer _to_ the pointer you want stored.
41 */
42 const struct drm_framebuffer *silly_api_alert = fb;
43 WARN_ON(!kfifo_put(&priv->fb_unref, &silly_api_alert));
44 schedule_work(&priv->fb_unref_work);
45}
46
47void armada_drm_queue_unref_work(struct drm_device *dev,
48 struct drm_framebuffer *fb)
49{
50 unsigned long flags;
51
52 spin_lock_irqsave(&dev->event_lock, flags);
53 __armada_drm_queue_unref_work(dev, fb);
54 spin_unlock_irqrestore(&dev->event_lock, flags);
55}
56
57static int armada_drm_load(struct drm_device *dev, unsigned long flags)
58{
59 const struct platform_device_id *id;
60 struct armada_private *priv;
61 struct resource *res[ARRAY_SIZE(priv->dcrtc)];
62 struct resource *mem = NULL;
63 int ret, n, i;
64
65 memset(res, 0, sizeof(res));
66
67 for (n = i = 0; ; n++) {
68 struct resource *r = platform_get_resource(dev->platformdev,
69 IORESOURCE_MEM, n);
70 if (!r)
71 break;
72
73 /* Resources above 64K are graphics memory */
74 if (resource_size(r) > SZ_64K)
75 mem = r;
76 else if (i < ARRAY_SIZE(priv->dcrtc))
77 res[i++] = r;
78 else
79 return -EINVAL;
80 }
81
82 if (!res[0] || !mem)
83 return -ENXIO;
84
85 if (!devm_request_mem_region(dev->dev, mem->start,
86 resource_size(mem), "armada-drm"))
87 return -EBUSY;
88
89 priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
90 if (!priv) {
91 DRM_ERROR("failed to allocate private\n");
92 return -ENOMEM;
93 }
94
95 dev->dev_private = priv;
96
97 /* Get the implementation specific driver data. */
98 id = platform_get_device_id(dev->platformdev);
99 if (!id)
100 return -ENXIO;
101
102 priv->variant = (struct armada_variant *)id->driver_data;
103
104 ret = priv->variant->init(priv, dev->dev);
105 if (ret)
106 return ret;
107
108 INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
109 INIT_KFIFO(priv->fb_unref);
110
111 /* Mode setting support */
112 drm_mode_config_init(dev);
113 dev->mode_config.min_width = 320;
114 dev->mode_config.min_height = 200;
115
116 /*
117 * With vscale enabled, the maximum width is 1920 due to the
118 * 1920 by 3 lines RAM
119 */
120 dev->mode_config.max_width = 1920;
121 dev->mode_config.max_height = 2048;
122
123 dev->mode_config.preferred_depth = 24;
124 dev->mode_config.funcs = &armada_drm_mode_config_funcs;
125 drm_mm_init(&priv->linear, mem->start, resource_size(mem));
126
127 /* Create all LCD controllers */
128 for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
129 if (!res[n])
130 break;
131
132 ret = armada_drm_crtc_create(dev, n, res[n]);
133 if (ret)
134 goto err_kms;
135 }
136
137 ret = drm_vblank_init(dev, n);
138 if (ret)
139 goto err_kms;
140
141 ret = drm_irq_install(dev);
142 if (ret)
143 goto err_kms;
144
145 dev->vblank_disable_allowed = 1;
146
147 ret = armada_fbdev_init(dev);
148 if (ret)
149 goto err_irq;
150
151 drm_kms_helper_poll_init(dev);
152
153 return 0;
154
155 err_irq:
156 drm_irq_uninstall(dev);
157 err_kms:
158 drm_mode_config_cleanup(dev);
159 drm_mm_takedown(&priv->linear);
160 flush_work(&priv->fb_unref_work);
161
162 return ret;
163}
164
165static int armada_drm_unload(struct drm_device *dev)
166{
167 struct armada_private *priv = dev->dev_private;
168
169 drm_kms_helper_poll_fini(dev);
170 armada_fbdev_fini(dev);
171 drm_irq_uninstall(dev);
172 drm_mode_config_cleanup(dev);
173 drm_mm_takedown(&priv->linear);
174 flush_work(&priv->fb_unref_work);
175 dev->dev_private = NULL;
176
177 return 0;
178}
179
180void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
181 struct armada_vbl_event *evt)
182{
183 unsigned long flags;
184
185 spin_lock_irqsave(&dcrtc->irq_lock, flags);
186 if (list_empty(&evt->node)) {
187 list_add_tail(&evt->node, &dcrtc->vbl_list);
188
189 drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
190 }
191 spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
192}
193
194void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
195 struct armada_vbl_event *evt)
196{
197 if (!list_empty(&evt->node)) {
198 list_del_init(&evt->node);
199 drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
200 }
201}
202
203void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *dcrtc,
204 struct armada_vbl_event *evt)
205{
206 unsigned long flags;
207
208 spin_lock_irqsave(&dcrtc->irq_lock, flags);
209 armada_drm_vbl_event_remove(dcrtc, evt);
210 spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
211}
212
213/* These are called under the vbl_lock. */
214static int armada_drm_enable_vblank(struct drm_device *dev, int crtc)
215{
216 struct armada_private *priv = dev->dev_private;
217 armada_drm_crtc_enable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
218 return 0;
219}
220
221static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
222{
223 struct armada_private *priv = dev->dev_private;
224 armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
225}
226
227static irqreturn_t armada_drm_irq_handler(int irq, void *arg)
228{
229 struct drm_device *dev = arg;
230 struct armada_private *priv = dev->dev_private;
231 struct armada_crtc *dcrtc = priv->dcrtc[0];
232 uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
233 irqreturn_t handled = IRQ_NONE;
234
235 /*
236 * This is rediculous - rather than writing bits to clear, we
237 * have to set the actual status register value. This is racy.
238 */
239 writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
240
241 /* Mask out those interrupts we haven't enabled */
242 v = stat & dcrtc->irq_ena;
243
244 if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
245 armada_drm_crtc_irq(dcrtc, stat);
246 handled = IRQ_HANDLED;
247 }
248
249 return handled;
250}
251
252static int armada_drm_irq_postinstall(struct drm_device *dev)
253{
254 struct armada_private *priv = dev->dev_private;
255 struct armada_crtc *dcrtc = priv->dcrtc[0];
256
257 spin_lock_irq(&dev->vbl_lock);
258 writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
259 writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
260 spin_unlock_irq(&dev->vbl_lock);
261
262 return 0;
263}
264
265static void armada_drm_irq_uninstall(struct drm_device *dev)
266{
267 struct armada_private *priv = dev->dev_private;
268 struct armada_crtc *dcrtc = priv->dcrtc[0];
269
270 writel(0, dcrtc->base + LCD_SPU_IRQ_ENA);
271}
272
273static struct drm_ioctl_desc armada_ioctls[] = {
274 DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,
275 DRM_UNLOCKED),
276 DRM_IOCTL_DEF_DRV(ARMADA_GEM_MMAP, armada_gem_mmap_ioctl,
277 DRM_UNLOCKED),
278 DRM_IOCTL_DEF_DRV(ARMADA_GEM_PWRITE, armada_gem_pwrite_ioctl,
279 DRM_UNLOCKED),
280};
281
282static const struct file_operations armada_drm_fops = {
283 .owner = THIS_MODULE,
284 .llseek = no_llseek,
285 .read = drm_read,
286 .poll = drm_poll,
287 .unlocked_ioctl = drm_ioctl,
288 .mmap = drm_gem_mmap,
289 .open = drm_open,
290 .release = drm_release,
291};
292
293static struct drm_driver armada_drm_driver = {
294 .load = armada_drm_load,
295 .open = NULL,
296 .preclose = NULL,
297 .postclose = NULL,
298 .lastclose = NULL,
299 .unload = armada_drm_unload,
300 .get_vblank_counter = drm_vblank_count,
301 .enable_vblank = armada_drm_enable_vblank,
302 .disable_vblank = armada_drm_disable_vblank,
303 .irq_handler = armada_drm_irq_handler,
304 .irq_postinstall = armada_drm_irq_postinstall,
305 .irq_uninstall = armada_drm_irq_uninstall,
306#ifdef CONFIG_DEBUG_FS
307 .debugfs_init = armada_drm_debugfs_init,
308 .debugfs_cleanup = armada_drm_debugfs_cleanup,
309#endif
310 .gem_init_object = NULL,
311 .gem_free_object = armada_gem_free_object,
312 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
313 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
314 .gem_prime_export = armada_gem_prime_export,
315 .gem_prime_import = armada_gem_prime_import,
316 .dumb_create = armada_gem_dumb_create,
317 .dumb_map_offset = armada_gem_dumb_map_offset,
318 .dumb_destroy = armada_gem_dumb_destroy,
319 .gem_vm_ops = &armada_gem_vm_ops,
320 .major = 1,
321 .minor = 0,
322 .name = "armada-drm",
323 .desc = "Armada SoC DRM",
324 .date = "20120730",
325 .driver_features = DRIVER_GEM | DRIVER_MODESET |
326 DRIVER_HAVE_IRQ | DRIVER_PRIME,
327 .ioctls = armada_ioctls,
328 .fops = &armada_drm_fops,
329};
330
331static int armada_drm_probe(struct platform_device *pdev)
332{
333 return drm_platform_init(&armada_drm_driver, pdev);
334}
335
336static int armada_drm_remove(struct platform_device *pdev)
337{
338 drm_platform_exit(&armada_drm_driver, pdev);
339 return 0;
340}
341
342static const struct platform_device_id armada_drm_platform_ids[] = {
343 {
344 .name = "armada-drm",
345 .driver_data = (unsigned long)&armada510_ops,
346 }, {
347 .name = "armada-510-drm",
348 .driver_data = (unsigned long)&armada510_ops,
349 },
350 { },
351};
352MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids);
353
354static struct platform_driver armada_drm_platform_driver = {
355 .probe = armada_drm_probe,
356 .remove = armada_drm_remove,
357 .driver = {
358 .name = "armada-drm",
359 .owner = THIS_MODULE,
360 },
361 .id_table = armada_drm_platform_ids,
362};
363
364static int __init armada_drm_init(void)
365{
366 armada_drm_driver.num_ioctls = DRM_ARRAY_SIZE(armada_ioctls);
367 return platform_driver_register(&armada_drm_platform_driver);
368}
369module_init(armada_drm_init);
370
371static void __exit armada_drm_exit(void)
372{
373 platform_driver_unregister(&armada_drm_platform_driver);
374}
375module_exit(armada_drm_exit);
376
377MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
378MODULE_DESCRIPTION("Armada DRM Driver");
379MODULE_LICENSE("GPL");
380MODULE_ALIAS("platform:armada-drm");
diff --git a/drivers/gpu/drm/armada/armada_fb.c b/drivers/gpu/drm/armada/armada_fb.c
new file mode 100644
index 000000000000..1c90969def3e
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_fb.c
@@ -0,0 +1,170 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#include <drm/drmP.h>
9#include <drm/drm_crtc_helper.h>
10#include <drm/drm_fb_helper.h>
11#include "armada_drm.h"
12#include "armada_fb.h"
13#include "armada_gem.h"
14#include "armada_hw.h"
15
16static void armada_fb_destroy(struct drm_framebuffer *fb)
17{
18 struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb);
19
20 drm_framebuffer_cleanup(&dfb->fb);
21 drm_gem_object_unreference_unlocked(&dfb->obj->obj);
22 kfree(dfb);
23}
24
25static int armada_fb_create_handle(struct drm_framebuffer *fb,
26 struct drm_file *dfile, unsigned int *handle)
27{
28 struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb);
29 return drm_gem_handle_create(dfile, &dfb->obj->obj, handle);
30}
31
32static const struct drm_framebuffer_funcs armada_fb_funcs = {
33 .destroy = armada_fb_destroy,
34 .create_handle = armada_fb_create_handle,
35};
36
37struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev,
38 struct drm_mode_fb_cmd2 *mode, struct armada_gem_object *obj)
39{
40 struct armada_framebuffer *dfb;
41 uint8_t format, config;
42 int ret;
43
44 switch (mode->pixel_format) {
45#define FMT(drm, fmt, mod) \
46 case DRM_FORMAT_##drm: \
47 format = CFG_##fmt; \
48 config = mod; \
49 break
50 FMT(RGB565, 565, CFG_SWAPRB);
51 FMT(BGR565, 565, 0);
52 FMT(ARGB1555, 1555, CFG_SWAPRB);
53 FMT(ABGR1555, 1555, 0);
54 FMT(RGB888, 888PACK, CFG_SWAPRB);
55 FMT(BGR888, 888PACK, 0);
56 FMT(XRGB8888, X888, CFG_SWAPRB);
57 FMT(XBGR8888, X888, 0);
58 FMT(ARGB8888, 8888, CFG_SWAPRB);
59 FMT(ABGR8888, 8888, 0);
60 FMT(YUYV, 422PACK, CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV);
61 FMT(UYVY, 422PACK, CFG_YUV2RGB);
62 FMT(VYUY, 422PACK, CFG_YUV2RGB | CFG_SWAPUV);
63 FMT(YVYU, 422PACK, CFG_YUV2RGB | CFG_SWAPYU);
64 FMT(YUV422, 422, CFG_YUV2RGB);
65 FMT(YVU422, 422, CFG_YUV2RGB | CFG_SWAPUV);
66 FMT(YUV420, 420, CFG_YUV2RGB);
67 FMT(YVU420, 420, CFG_YUV2RGB | CFG_SWAPUV);
68 FMT(C8, PSEUDO8, 0);
69#undef FMT
70 default:
71 return ERR_PTR(-EINVAL);
72 }
73
74 dfb = kzalloc(sizeof(*dfb), GFP_KERNEL);
75 if (!dfb) {
76 DRM_ERROR("failed to allocate Armada fb object\n");
77 return ERR_PTR(-ENOMEM);
78 }
79
80 dfb->fmt = format;
81 dfb->mod = config;
82 dfb->obj = obj;
83
84 drm_helper_mode_fill_fb_struct(&dfb->fb, mode);
85
86 ret = drm_framebuffer_init(dev, &dfb->fb, &armada_fb_funcs);
87 if (ret) {
88 kfree(dfb);
89 return ERR_PTR(ret);
90 }
91
92 /*
93 * Take a reference on our object as we're successful - the
94 * caller already holds a reference, which keeps us safe for
95 * the above call, but the caller will drop their reference
96 * to it. Hence we need to take our own reference.
97 */
98 drm_gem_object_reference(&obj->obj);
99
100 return dfb;
101}
102
103static struct drm_framebuffer *armada_fb_create(struct drm_device *dev,
104 struct drm_file *dfile, struct drm_mode_fb_cmd2 *mode)
105{
106 struct armada_gem_object *obj;
107 struct armada_framebuffer *dfb;
108 int ret;
109
110 DRM_DEBUG_DRIVER("w%u h%u pf%08x f%u p%u,%u,%u\n",
111 mode->width, mode->height, mode->pixel_format,
112 mode->flags, mode->pitches[0], mode->pitches[1],
113 mode->pitches[2]);
114
115 /* We can only handle a single plane at the moment */
116 if (drm_format_num_planes(mode->pixel_format) > 1 &&
117 (mode->handles[0] != mode->handles[1] ||
118 mode->handles[0] != mode->handles[2])) {
119 ret = -EINVAL;
120 goto err;
121 }
122
123 obj = armada_gem_object_lookup(dev, dfile, mode->handles[0]);
124 if (!obj) {
125 ret = -ENOENT;
126 goto err;
127 }
128
129 if (obj->obj.import_attach && !obj->sgt) {
130 ret = armada_gem_map_import(obj);
131 if (ret)
132 goto err_unref;
133 }
134
135 /* Framebuffer objects must have a valid device address for scanout */
136 if (obj->dev_addr == DMA_ERROR_CODE) {
137 ret = -EINVAL;
138 goto err_unref;
139 }
140
141 dfb = armada_framebuffer_create(dev, mode, obj);
142 if (IS_ERR(dfb)) {
143 ret = PTR_ERR(dfb);
144 goto err;
145 }
146
147 drm_gem_object_unreference_unlocked(&obj->obj);
148
149 return &dfb->fb;
150
151 err_unref:
152 drm_gem_object_unreference_unlocked(&obj->obj);
153 err:
154 DRM_ERROR("failed to initialize framebuffer: %d\n", ret);
155 return ERR_PTR(ret);
156}
157
158static void armada_output_poll_changed(struct drm_device *dev)
159{
160 struct armada_private *priv = dev->dev_private;
161 struct drm_fb_helper *fbh = priv->fbdev;
162
163 if (fbh)
164 drm_fb_helper_hotplug_event(fbh);
165}
166
167const struct drm_mode_config_funcs armada_drm_mode_config_funcs = {
168 .fb_create = armada_fb_create,
169 .output_poll_changed = armada_output_poll_changed,
170};
diff --git a/drivers/gpu/drm/armada/armada_fb.h b/drivers/gpu/drm/armada/armada_fb.h
new file mode 100644
index 000000000000..ce3f12ebfc53
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_fb.h
@@ -0,0 +1,24 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#ifndef ARMADA_FB_H
9#define ARMADA_FB_H
10
11struct armada_framebuffer {
12 struct drm_framebuffer fb;
13 struct armada_gem_object *obj;
14 uint8_t fmt;
15 uint8_t mod;
16};
17#define drm_fb_to_armada_fb(dfb) \
18 container_of(dfb, struct armada_framebuffer, fb)
19#define drm_fb_obj(fb) drm_fb_to_armada_fb(fb)->obj
20
21struct armada_framebuffer *armada_framebuffer_create(struct drm_device *,
22 struct drm_mode_fb_cmd2 *, struct armada_gem_object *);
23
24#endif
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
new file mode 100644
index 000000000000..dd5ea77dac96
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -0,0 +1,202 @@
1/*
2 * Copyright (C) 2012 Russell King
3 * Written from the i915 driver.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9#include <linux/errno.h>
10#include <linux/fb.h>
11#include <linux/kernel.h>
12#include <linux/module.h>
13
14#include <drm/drmP.h>
15#include <drm/drm_fb_helper.h>
16#include "armada_crtc.h"
17#include "armada_drm.h"
18#include "armada_fb.h"
19#include "armada_gem.h"
20
21static /*const*/ struct fb_ops armada_fb_ops = {
22 .owner = THIS_MODULE,
23 .fb_check_var = drm_fb_helper_check_var,
24 .fb_set_par = drm_fb_helper_set_par,
25 .fb_fillrect = cfb_fillrect,
26 .fb_copyarea = cfb_copyarea,
27 .fb_imageblit = cfb_imageblit,
28 .fb_pan_display = drm_fb_helper_pan_display,
29 .fb_blank = drm_fb_helper_blank,
30 .fb_setcmap = drm_fb_helper_setcmap,
31 .fb_debug_enter = drm_fb_helper_debug_enter,
32 .fb_debug_leave = drm_fb_helper_debug_leave,
33};
34
35static int armada_fb_create(struct drm_fb_helper *fbh,
36 struct drm_fb_helper_surface_size *sizes)
37{
38 struct drm_device *dev = fbh->dev;
39 struct drm_mode_fb_cmd2 mode;
40 struct armada_framebuffer *dfb;
41 struct armada_gem_object *obj;
42 struct fb_info *info;
43 int size, ret;
44 void *ptr;
45
46 memset(&mode, 0, sizeof(mode));
47 mode.width = sizes->surface_width;
48 mode.height = sizes->surface_height;
49 mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp);
50 mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
51 sizes->surface_depth);
52
53 size = mode.pitches[0] * mode.height;
54 obj = armada_gem_alloc_private_object(dev, size);
55 if (!obj) {
56 DRM_ERROR("failed to allocate fb memory\n");
57 return -ENOMEM;
58 }
59
60 ret = armada_gem_linear_back(dev, obj);
61 if (ret) {
62 drm_gem_object_unreference_unlocked(&obj->obj);
63 return ret;
64 }
65
66 ptr = armada_gem_map_object(dev, obj);
67 if (!ptr) {
68 drm_gem_object_unreference_unlocked(&obj->obj);
69 return -ENOMEM;
70 }
71
72 dfb = armada_framebuffer_create(dev, &mode, obj);
73
74 /*
75 * A reference is now held by the framebuffer object if
76 * successful, otherwise this drops the ref for the error path.
77 */
78 drm_gem_object_unreference_unlocked(&obj->obj);
79
80 if (IS_ERR(dfb))
81 return PTR_ERR(dfb);
82
83 info = framebuffer_alloc(0, dev->dev);
84 if (!info) {
85 ret = -ENOMEM;
86 goto err_fballoc;
87 }
88
89 ret = fb_alloc_cmap(&info->cmap, 256, 0);
90 if (ret) {
91 ret = -ENOMEM;
92 goto err_fbcmap;
93 }
94
95 strlcpy(info->fix.id, "armada-drmfb", sizeof(info->fix.id));
96 info->par = fbh;
97 info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
98 info->fbops = &armada_fb_ops;
99 info->fix.smem_start = obj->phys_addr;
100 info->fix.smem_len = obj->obj.size;
101 info->screen_size = obj->obj.size;
102 info->screen_base = ptr;
103 fbh->fb = &dfb->fb;
104 fbh->fbdev = info;
105 drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], dfb->fb.depth);
106 drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height);
107
108 DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08x\n",
109 dfb->fb.width, dfb->fb.height,
110 dfb->fb.bits_per_pixel, obj->phys_addr);
111
112 return 0;
113
114 err_fbcmap:
115 framebuffer_release(info);
116 err_fballoc:
117 dfb->fb.funcs->destroy(&dfb->fb);
118 return ret;
119}
120
121static int armada_fb_probe(struct drm_fb_helper *fbh,
122 struct drm_fb_helper_surface_size *sizes)
123{
124 int ret = 0;
125
126 if (!fbh->fb) {
127 ret = armada_fb_create(fbh, sizes);
128 if (ret == 0)
129 ret = 1;
130 }
131 return ret;
132}
133
134static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
135 .gamma_set = armada_drm_crtc_gamma_set,
136 .gamma_get = armada_drm_crtc_gamma_get,
137 .fb_probe = armada_fb_probe,
138};
139
140int armada_fbdev_init(struct drm_device *dev)
141{
142 struct armada_private *priv = dev->dev_private;
143 struct drm_fb_helper *fbh;
144 int ret;
145
146 fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL);
147 if (!fbh)
148 return -ENOMEM;
149
150 priv->fbdev = fbh;
151
152 fbh->funcs = &armada_fb_helper_funcs;
153
154 ret = drm_fb_helper_init(dev, fbh, 1, 1);
155 if (ret) {
156 DRM_ERROR("failed to initialize drm fb helper\n");
157 goto err_fb_helper;
158 }
159
160 ret = drm_fb_helper_single_add_all_connectors(fbh);
161 if (ret) {
162 DRM_ERROR("failed to add fb connectors\n");
163 goto err_fb_setup;
164 }
165
166 ret = drm_fb_helper_initial_config(fbh, 32);
167 if (ret) {
168 DRM_ERROR("failed to set initial config\n");
169 goto err_fb_setup;
170 }
171
172 return 0;
173 err_fb_setup:
174 drm_fb_helper_fini(fbh);
175 err_fb_helper:
176 priv->fbdev = NULL;
177 return ret;
178}
179
180void armada_fbdev_fini(struct drm_device *dev)
181{
182 struct armada_private *priv = dev->dev_private;
183 struct drm_fb_helper *fbh = priv->fbdev;
184
185 if (fbh) {
186 struct fb_info *info = fbh->fbdev;
187
188 if (info) {
189 unregister_framebuffer(info);
190 if (info->cmap.len)
191 fb_dealloc_cmap(&info->cmap);
192 framebuffer_release(info);
193 }
194
195 if (fbh->fb)
196 fbh->fb->funcs->destroy(fbh->fb);
197
198 drm_fb_helper_fini(fbh);
199
200 priv->fbdev = NULL;
201 }
202}
diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
new file mode 100644
index 000000000000..9f2356bae7fd
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_gem.c
@@ -0,0 +1,611 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#include <linux/dma-buf.h>
9#include <linux/dma-mapping.h>
10#include <linux/shmem_fs.h>
11#include <drm/drmP.h>
12#include "armada_drm.h"
13#include "armada_gem.h"
14#include <drm/armada_drm.h>
15#include "armada_ioctlP.h"
16
17static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
18{
19 struct armada_gem_object *obj = drm_to_armada_gem(vma->vm_private_data);
20 unsigned long addr = (unsigned long)vmf->virtual_address;
21 unsigned long pfn = obj->phys_addr >> PAGE_SHIFT;
22 int ret;
23
24 pfn += (addr - vma->vm_start) >> PAGE_SHIFT;
25 ret = vm_insert_pfn(vma, addr, pfn);
26
27 switch (ret) {
28 case 0:
29 case -EBUSY:
30 return VM_FAULT_NOPAGE;
31 case -ENOMEM:
32 return VM_FAULT_OOM;
33 default:
34 return VM_FAULT_SIGBUS;
35 }
36}
37
38const struct vm_operations_struct armada_gem_vm_ops = {
39 .fault = armada_gem_vm_fault,
40 .open = drm_gem_vm_open,
41 .close = drm_gem_vm_close,
42};
43
44static size_t roundup_gem_size(size_t size)
45{
46 return roundup(size, PAGE_SIZE);
47}
48
49/* dev->struct_mutex is held here */
50void armada_gem_free_object(struct drm_gem_object *obj)
51{
52 struct armada_gem_object *dobj = drm_to_armada_gem(obj);
53
54 DRM_DEBUG_DRIVER("release obj %p\n", dobj);
55
56 drm_gem_free_mmap_offset(&dobj->obj);
57
58 if (dobj->page) {
59 /* page backed memory */
60 unsigned int order = get_order(dobj->obj.size);
61 __free_pages(dobj->page, order);
62 } else if (dobj->linear) {
63 /* linear backed memory */
64 drm_mm_remove_node(dobj->linear);
65 kfree(dobj->linear);
66 if (dobj->addr)
67 iounmap(dobj->addr);
68 }
69
70 if (dobj->obj.import_attach) {
71 /* We only ever display imported data */
72 dma_buf_unmap_attachment(dobj->obj.import_attach, dobj->sgt,
73 DMA_TO_DEVICE);
74 drm_prime_gem_destroy(&dobj->obj, NULL);
75 }
76
77 drm_gem_object_release(&dobj->obj);
78
79 kfree(dobj);
80}
81
82int
83armada_gem_linear_back(struct drm_device *dev, struct armada_gem_object *obj)
84{
85 struct armada_private *priv = dev->dev_private;
86 size_t size = obj->obj.size;
87
88 if (obj->page || obj->linear)
89 return 0;
90
91 /*
92 * If it is a small allocation (typically cursor, which will
93 * be 32x64 or 64x32 ARGB pixels) try to get it from the system.
94 * Framebuffers will never be this small (our minimum size for
95 * framebuffers is larger than this anyway.) Such objects are
96 * only accessed by the CPU so we don't need any special handing
97 * here.
98 */
99 if (size <= 8192) {
100 unsigned int order = get_order(size);
101 struct page *p = alloc_pages(GFP_KERNEL, order);
102
103 if (p) {
104 obj->addr = page_address(p);
105 obj->phys_addr = page_to_phys(p);
106 obj->page = p;
107
108 memset(obj->addr, 0, PAGE_ALIGN(size));
109 }
110 }
111
112 /*
113 * We could grab something from CMA if it's enabled, but that
114 * involves building in a problem:
115 *
116 * CMA's interface uses dma_alloc_coherent(), which provides us
117 * with an CPU virtual address and a device address.
118 *
119 * The CPU virtual address may be either an address in the kernel
120 * direct mapped region (for example, as it would be on x86) or
121 * it may be remapped into another part of kernel memory space
122 * (eg, as it would be on ARM.) This means virt_to_phys() on the
123 * returned virtual address is invalid depending on the architecture
124 * implementation.
125 *
126 * The device address may also not be a physical address; it may
127 * be that there is some kind of remapping between the device and
128 * system RAM, which makes the use of the device address also
129 * unsafe to re-use as a physical address.
130 *
131 * This makes DRM usage of dma_alloc_coherent() in a generic way
132 * at best very questionable and unsafe.
133 */
134
135 /* Otherwise, grab it from our linear allocation */
136 if (!obj->page) {
137 struct drm_mm_node *node;
138 unsigned align = min_t(unsigned, size, SZ_2M);
139 void __iomem *ptr;
140 int ret;
141
142 node = kzalloc(sizeof(*node), GFP_KERNEL);
143 if (!node)
144 return -ENOSPC;
145
146 mutex_lock(&dev->struct_mutex);
147 ret = drm_mm_insert_node(&priv->linear, node, size, align,
148 DRM_MM_SEARCH_DEFAULT);
149 mutex_unlock(&dev->struct_mutex);
150 if (ret) {
151 kfree(node);
152 return ret;
153 }
154
155 obj->linear = node;
156
157 /* Ensure that the memory we're returning is cleared. */
158 ptr = ioremap_wc(obj->linear->start, size);
159 if (!ptr) {
160 mutex_lock(&dev->struct_mutex);
161 drm_mm_remove_node(obj->linear);
162 mutex_unlock(&dev->struct_mutex);
163 kfree(obj->linear);
164 obj->linear = NULL;
165 return -ENOMEM;
166 }
167
168 memset_io(ptr, 0, size);
169 iounmap(ptr);
170
171 obj->phys_addr = obj->linear->start;
172 obj->dev_addr = obj->linear->start;
173 }
174
175 DRM_DEBUG_DRIVER("obj %p phys %#x dev %#x\n",
176 obj, obj->phys_addr, obj->dev_addr);
177
178 return 0;
179}
180
181void *
182armada_gem_map_object(struct drm_device *dev, struct armada_gem_object *dobj)
183{
184 /* only linear objects need to be ioremap'd */
185 if (!dobj->addr && dobj->linear)
186 dobj->addr = ioremap_wc(dobj->phys_addr, dobj->obj.size);
187 return dobj->addr;
188}
189
190struct armada_gem_object *
191armada_gem_alloc_private_object(struct drm_device *dev, size_t size)
192{
193 struct armada_gem_object *obj;
194
195 size = roundup_gem_size(size);
196
197 obj = kzalloc(sizeof(*obj), GFP_KERNEL);
198 if (!obj)
199 return NULL;
200
201 drm_gem_private_object_init(dev, &obj->obj, size);
202 obj->dev_addr = DMA_ERROR_CODE;
203
204 DRM_DEBUG_DRIVER("alloc private obj %p size %zu\n", obj, size);
205
206 return obj;
207}
208
209struct armada_gem_object *armada_gem_alloc_object(struct drm_device *dev,
210 size_t size)
211{
212 struct armada_gem_object *obj;
213 struct address_space *mapping;
214
215 size = roundup_gem_size(size);
216
217 obj = kzalloc(sizeof(*obj), GFP_KERNEL);
218 if (!obj)
219 return NULL;
220
221 if (drm_gem_object_init(dev, &obj->obj, size)) {
222 kfree(obj);
223 return NULL;
224 }
225
226 obj->dev_addr = DMA_ERROR_CODE;
227
228 mapping = obj->obj.filp->f_path.dentry->d_inode->i_mapping;
229 mapping_set_gfp_mask(mapping, GFP_HIGHUSER | __GFP_RECLAIMABLE);
230
231 DRM_DEBUG_DRIVER("alloc obj %p size %zu\n", obj, size);
232
233 return obj;
234}
235
236/* Dumb alloc support */
237int armada_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
238 struct drm_mode_create_dumb *args)
239{
240 struct armada_gem_object *dobj;
241 u32 handle;
242 size_t size;
243 int ret;
244
245 args->pitch = armada_pitch(args->width, args->bpp);
246 args->size = size = args->pitch * args->height;
247
248 dobj = armada_gem_alloc_private_object(dev, size);
249 if (dobj == NULL)
250 return -ENOMEM;
251
252 ret = armada_gem_linear_back(dev, dobj);
253 if (ret)
254 goto err;
255
256 ret = drm_gem_handle_create(file, &dobj->obj, &handle);
257 if (ret)
258 goto err;
259
260 args->handle = handle;
261
262 /* drop reference from allocate - handle holds it now */
263 DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle);
264 err:
265 drm_gem_object_unreference_unlocked(&dobj->obj);
266 return ret;
267}
268
269int armada_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
270 uint32_t handle, uint64_t *offset)
271{
272 struct armada_gem_object *obj;
273 int ret = 0;
274
275 mutex_lock(&dev->struct_mutex);
276 obj = armada_gem_object_lookup(dev, file, handle);
277 if (!obj) {
278 DRM_ERROR("failed to lookup gem object\n");
279 ret = -EINVAL;
280 goto err_unlock;
281 }
282
283 /* Don't allow imported objects to be mapped */
284 if (obj->obj.import_attach) {
285 ret = -EINVAL;
286 goto err_unlock;
287 }
288
289 ret = drm_gem_create_mmap_offset(&obj->obj);
290 if (ret == 0) {
291 *offset = drm_vma_node_offset_addr(&obj->obj.vma_node);
292 DRM_DEBUG_DRIVER("handle %#x offset %llx\n", handle, *offset);
293 }
294
295 drm_gem_object_unreference(&obj->obj);
296 err_unlock:
297 mutex_unlock(&dev->struct_mutex);
298
299 return ret;
300}
301
302int armada_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
303 uint32_t handle)
304{
305 return drm_gem_handle_delete(file, handle);
306}
307
308/* Private driver gem ioctls */
309int armada_gem_create_ioctl(struct drm_device *dev, void *data,
310 struct drm_file *file)
311{
312 struct drm_armada_gem_create *args = data;
313 struct armada_gem_object *dobj;
314 size_t size;
315 u32 handle;
316 int ret;
317
318 if (args->size == 0)
319 return -ENOMEM;
320
321 size = args->size;
322
323 dobj = armada_gem_alloc_object(dev, size);
324 if (dobj == NULL)
325 return -ENOMEM;
326
327 ret = drm_gem_handle_create(file, &dobj->obj, &handle);
328 if (ret)
329 goto err;
330
331 args->handle = handle;
332
333 /* drop reference from allocate - handle holds it now */
334 DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle);
335 err:
336 drm_gem_object_unreference_unlocked(&dobj->obj);
337 return ret;
338}
339
340/* Map a shmem-backed object into process memory space */
341int armada_gem_mmap_ioctl(struct drm_device *dev, void *data,
342 struct drm_file *file)
343{
344 struct drm_armada_gem_mmap *args = data;
345 struct armada_gem_object *dobj;
346 unsigned long addr;
347
348 dobj = armada_gem_object_lookup(dev, file, args->handle);
349 if (dobj == NULL)
350 return -ENOENT;
351
352 if (!dobj->obj.filp) {
353 drm_gem_object_unreference(&dobj->obj);
354 return -EINVAL;
355 }
356
357 addr = vm_mmap(dobj->obj.filp, 0, args->size, PROT_READ | PROT_WRITE,
358 MAP_SHARED, args->offset);
359 drm_gem_object_unreference(&dobj->obj);
360 if (IS_ERR_VALUE(addr))
361 return addr;
362
363 args->addr = addr;
364
365 return 0;
366}
367
368int armada_gem_pwrite_ioctl(struct drm_device *dev, void *data,
369 struct drm_file *file)
370{
371 struct drm_armada_gem_pwrite *args = data;
372 struct armada_gem_object *dobj;
373 char __user *ptr;
374 int ret;
375
376 DRM_DEBUG_DRIVER("handle %u off %u size %u ptr 0x%llx\n",
377 args->handle, args->offset, args->size, args->ptr);
378
379 if (args->size == 0)
380 return 0;
381
382 ptr = (char __user *)(uintptr_t)args->ptr;
383
384 if (!access_ok(VERIFY_READ, ptr, args->size))
385 return -EFAULT;
386
387 ret = fault_in_multipages_readable(ptr, args->size);
388 if (ret)
389 return ret;
390
391 dobj = armada_gem_object_lookup(dev, file, args->handle);
392 if (dobj == NULL)
393 return -ENOENT;
394
395 /* Must be a kernel-mapped object */
396 if (!dobj->addr)
397 return -EINVAL;
398
399 if (args->offset > dobj->obj.size ||
400 args->size > dobj->obj.size - args->offset) {
401 DRM_ERROR("invalid size: object size %u\n", dobj->obj.size);
402 ret = -EINVAL;
403 goto unref;
404 }
405
406 if (copy_from_user(dobj->addr + args->offset, ptr, args->size)) {
407 ret = -EFAULT;
408 } else if (dobj->update) {
409 dobj->update(dobj->update_data);
410 ret = 0;
411 }
412
413 unref:
414 drm_gem_object_unreference_unlocked(&dobj->obj);
415 return ret;
416}
417
418/* Prime support */
419struct sg_table *
420armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
421 enum dma_data_direction dir)
422{
423 struct drm_gem_object *obj = attach->dmabuf->priv;
424 struct armada_gem_object *dobj = drm_to_armada_gem(obj);
425 struct scatterlist *sg;
426 struct sg_table *sgt;
427 int i, num;
428
429 sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
430 if (!sgt)
431 return NULL;
432
433 if (dobj->obj.filp) {
434 struct address_space *mapping;
435 gfp_t gfp;
436 int count;
437
438 count = dobj->obj.size / PAGE_SIZE;
439 if (sg_alloc_table(sgt, count, GFP_KERNEL))
440 goto free_sgt;
441
442 mapping = file_inode(dobj->obj.filp)->i_mapping;
443 gfp = mapping_gfp_mask(mapping);
444
445 for_each_sg(sgt->sgl, sg, count, i) {
446 struct page *page;
447
448 page = shmem_read_mapping_page_gfp(mapping, i, gfp);
449 if (IS_ERR(page)) {
450 num = i;
451 goto release;
452 }
453
454 sg_set_page(sg, page, PAGE_SIZE, 0);
455 }
456
457 if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0) {
458 num = sgt->nents;
459 goto release;
460 }
461 } else if (dobj->page) {
462 /* Single contiguous page */
463 if (sg_alloc_table(sgt, 1, GFP_KERNEL))
464 goto free_sgt;
465
466 sg_set_page(sgt->sgl, dobj->page, dobj->obj.size, 0);
467
468 if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
469 goto free_table;
470 } else if (dobj->linear) {
471 /* Single contiguous physical region - no struct page */
472 if (sg_alloc_table(sgt, 1, GFP_KERNEL))
473 goto free_sgt;
474 sg_dma_address(sgt->sgl) = dobj->dev_addr;
475 sg_dma_len(sgt->sgl) = dobj->obj.size;
476 } else {
477 goto free_sgt;
478 }
479 return sgt;
480
481 release:
482 for_each_sg(sgt->sgl, sg, num, i)
483 page_cache_release(sg_page(sg));
484 free_table:
485 sg_free_table(sgt);
486 free_sgt:
487 kfree(sgt);
488 return NULL;
489}
490
491static void armada_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach,
492 struct sg_table *sgt, enum dma_data_direction dir)
493{
494 struct drm_gem_object *obj = attach->dmabuf->priv;
495 struct armada_gem_object *dobj = drm_to_armada_gem(obj);
496 int i;
497
498 if (!dobj->linear)
499 dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
500
501 if (dobj->obj.filp) {
502 struct scatterlist *sg;
503 for_each_sg(sgt->sgl, sg, sgt->nents, i)
504 page_cache_release(sg_page(sg));
505 }
506
507 sg_free_table(sgt);
508 kfree(sgt);
509}
510
511static void *armada_gem_dmabuf_no_kmap(struct dma_buf *buf, unsigned long n)
512{
513 return NULL;
514}
515
516static void
517armada_gem_dmabuf_no_kunmap(struct dma_buf *buf, unsigned long n, void *addr)
518{
519}
520
521static int
522armada_gem_dmabuf_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
523{
524 return -EINVAL;
525}
526
527static const struct dma_buf_ops armada_gem_prime_dmabuf_ops = {
528 .map_dma_buf = armada_gem_prime_map_dma_buf,
529 .unmap_dma_buf = armada_gem_prime_unmap_dma_buf,
530 .release = drm_gem_dmabuf_release,
531 .kmap_atomic = armada_gem_dmabuf_no_kmap,
532 .kunmap_atomic = armada_gem_dmabuf_no_kunmap,
533 .kmap = armada_gem_dmabuf_no_kmap,
534 .kunmap = armada_gem_dmabuf_no_kunmap,
535 .mmap = armada_gem_dmabuf_mmap,
536};
537
538struct dma_buf *
539armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj,
540 int flags)
541{
542 return dma_buf_export(obj, &armada_gem_prime_dmabuf_ops, obj->size,
543 O_RDWR);
544}
545
546struct drm_gem_object *
547armada_gem_prime_import(struct drm_device *dev, struct dma_buf *buf)
548{
549 struct dma_buf_attachment *attach;
550 struct armada_gem_object *dobj;
551
552 if (buf->ops == &armada_gem_prime_dmabuf_ops) {
553 struct drm_gem_object *obj = buf->priv;
554 if (obj->dev == dev) {
555 /*
556 * Importing our own dmabuf(s) increases the
557 * refcount on the gem object itself.
558 */
559 drm_gem_object_reference(obj);
560 dma_buf_put(buf);
561 return obj;
562 }
563 }
564
565 attach = dma_buf_attach(buf, dev->dev);
566 if (IS_ERR(attach))
567 return ERR_CAST(attach);
568
569 dobj = armada_gem_alloc_private_object(dev, buf->size);
570 if (!dobj) {
571 dma_buf_detach(buf, attach);
572 return ERR_PTR(-ENOMEM);
573 }
574
575 dobj->obj.import_attach = attach;
576
577 /*
578 * Don't call dma_buf_map_attachment() here - it maps the
579 * scatterlist immediately for DMA, and this is not always
580 * an appropriate thing to do.
581 */
582 return &dobj->obj;
583}
584
585int armada_gem_map_import(struct armada_gem_object *dobj)
586{
587 int ret;
588
589 dobj->sgt = dma_buf_map_attachment(dobj->obj.import_attach,
590 DMA_TO_DEVICE);
591 if (!dobj->sgt) {
592 DRM_ERROR("dma_buf_map_attachment() returned NULL\n");
593 return -EINVAL;
594 }
595 if (IS_ERR(dobj->sgt)) {
596 ret = PTR_ERR(dobj->sgt);
597 dobj->sgt = NULL;
598 DRM_ERROR("dma_buf_map_attachment() error: %d\n", ret);
599 return ret;
600 }
601 if (dobj->sgt->nents > 1) {
602 DRM_ERROR("dma_buf_map_attachment() returned an (unsupported) scattered list\n");
603 return -EINVAL;
604 }
605 if (sg_dma_len(dobj->sgt->sgl) < dobj->obj.size) {
606 DRM_ERROR("dma_buf_map_attachment() returned a small buffer\n");
607 return -EINVAL;
608 }
609 dobj->dev_addr = sg_dma_address(dobj->sgt->sgl);
610 return 0;
611}
diff --git a/drivers/gpu/drm/armada/armada_gem.h b/drivers/gpu/drm/armada/armada_gem.h
new file mode 100644
index 000000000000..00b6cd461a03
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_gem.h
@@ -0,0 +1,52 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#ifndef ARMADA_GEM_H
9#define ARMADA_GEM_H
10
11/* GEM */
12struct armada_gem_object {
13 struct drm_gem_object obj;
14 void *addr;
15 phys_addr_t phys_addr;
16 resource_size_t dev_addr;
17 struct drm_mm_node *linear; /* for linear backed */
18 struct page *page; /* for page backed */
19 struct sg_table *sgt; /* for imported */
20 void (*update)(void *);
21 void *update_data;
22};
23
24extern const struct vm_operations_struct armada_gem_vm_ops;
25
26#define drm_to_armada_gem(o) container_of(o, struct armada_gem_object, obj)
27
28void armada_gem_free_object(struct drm_gem_object *);
29int armada_gem_linear_back(struct drm_device *, struct armada_gem_object *);
30void *armada_gem_map_object(struct drm_device *, struct armada_gem_object *);
31struct armada_gem_object *armada_gem_alloc_private_object(struct drm_device *,
32 size_t);
33int armada_gem_dumb_create(struct drm_file *, struct drm_device *,
34 struct drm_mode_create_dumb *);
35int armada_gem_dumb_map_offset(struct drm_file *, struct drm_device *,
36 uint32_t, uint64_t *);
37int armada_gem_dumb_destroy(struct drm_file *, struct drm_device *,
38 uint32_t);
39struct dma_buf *armada_gem_prime_export(struct drm_device *dev,
40 struct drm_gem_object *obj, int flags);
41struct drm_gem_object *armada_gem_prime_import(struct drm_device *,
42 struct dma_buf *);
43int armada_gem_map_import(struct armada_gem_object *);
44
45static inline struct armada_gem_object *armada_gem_object_lookup(
46 struct drm_device *dev, struct drm_file *dfile, unsigned handle)
47{
48 struct drm_gem_object *obj = drm_gem_object_lookup(dev, dfile, handle);
49
50 return obj ? drm_to_armada_gem(obj) : NULL;
51}
52#endif
diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h
new file mode 100644
index 000000000000..216a4b50c36b
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_hw.h
@@ -0,0 +1,316 @@
1/*
2 * Copyright (C) 2012 Russell King
3 * Rewritten from the dovefb driver, and Armada510 manuals.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9#ifndef ARMADA_HW_H
10#define ARMADA_HW_H
11
12/*
13 * Note: the following registers are written from IRQ context:
14 * LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL
15 * LCD_SPU_DMA_START_ADDR_[YUV][01], LCD_SPU_DMA_PITCH_YC,
16 * LCD_SPU_DMA_PITCH_UV, LCD_SPU_DMA_OVSA_HPXL_VLN,
17 * LCD_SPU_DMA_HPXL_VLN, LCD_SPU_DZM_HPXL_VLN, LCD_SPU_DMA_CTRL0
18 */
19enum {
20 LCD_SPU_ADV_REG = 0x0084, /* Armada 510 */
21 LCD_SPU_DMA_START_ADDR_Y0 = 0x00c0,
22 LCD_SPU_DMA_START_ADDR_U0 = 0x00c4,
23 LCD_SPU_DMA_START_ADDR_V0 = 0x00c8,
24 LCD_CFG_DMA_START_ADDR_0 = 0x00cc,
25 LCD_SPU_DMA_START_ADDR_Y1 = 0x00d0,
26 LCD_SPU_DMA_START_ADDR_U1 = 0x00d4,
27 LCD_SPU_DMA_START_ADDR_V1 = 0x00d8,
28 LCD_CFG_DMA_START_ADDR_1 = 0x00dc,
29 LCD_SPU_DMA_PITCH_YC = 0x00e0,
30 LCD_SPU_DMA_PITCH_UV = 0x00e4,
31 LCD_SPU_DMA_OVSA_HPXL_VLN = 0x00e8,
32 LCD_SPU_DMA_HPXL_VLN = 0x00ec,
33 LCD_SPU_DZM_HPXL_VLN = 0x00f0,
34 LCD_CFG_GRA_START_ADDR0 = 0x00f4,
35 LCD_CFG_GRA_START_ADDR1 = 0x00f8,
36 LCD_CFG_GRA_PITCH = 0x00fc,
37 LCD_SPU_GRA_OVSA_HPXL_VLN = 0x0100,
38 LCD_SPU_GRA_HPXL_VLN = 0x0104,
39 LCD_SPU_GZM_HPXL_VLN = 0x0108,
40 LCD_SPU_HWC_OVSA_HPXL_VLN = 0x010c,
41 LCD_SPU_HWC_HPXL_VLN = 0x0110,
42 LCD_SPUT_V_H_TOTAL = 0x0114,
43 LCD_SPU_V_H_ACTIVE = 0x0118,
44 LCD_SPU_H_PORCH = 0x011c,
45 LCD_SPU_V_PORCH = 0x0120,
46 LCD_SPU_BLANKCOLOR = 0x0124,
47 LCD_SPU_ALPHA_COLOR1 = 0x0128,
48 LCD_SPU_ALPHA_COLOR2 = 0x012c,
49 LCD_SPU_COLORKEY_Y = 0x0130,
50 LCD_SPU_COLORKEY_U = 0x0134,
51 LCD_SPU_COLORKEY_V = 0x0138,
52 LCD_CFG_RDREG4F = 0x013c, /* Armada 510 */
53 LCD_SPU_SPI_RXDATA = 0x0140,
54 LCD_SPU_ISA_RXDATA = 0x0144,
55 LCD_SPU_HWC_RDDAT = 0x0158,
56 LCD_SPU_GAMMA_RDDAT = 0x015c,
57 LCD_SPU_PALETTE_RDDAT = 0x0160,
58 LCD_SPU_IOPAD_IN = 0x0178,
59 LCD_CFG_RDREG5F = 0x017c,
60 LCD_SPU_SPI_CTRL = 0x0180,
61 LCD_SPU_SPI_TXDATA = 0x0184,
62 LCD_SPU_SMPN_CTRL = 0x0188,
63 LCD_SPU_DMA_CTRL0 = 0x0190,
64 LCD_SPU_DMA_CTRL1 = 0x0194,
65 LCD_SPU_SRAM_CTRL = 0x0198,
66 LCD_SPU_SRAM_WRDAT = 0x019c,
67 LCD_SPU_SRAM_PARA0 = 0x01a0, /* Armada 510 */
68 LCD_SPU_SRAM_PARA1 = 0x01a4,
69 LCD_CFG_SCLK_DIV = 0x01a8,
70 LCD_SPU_CONTRAST = 0x01ac,
71 LCD_SPU_SATURATION = 0x01b0,
72 LCD_SPU_CBSH_HUE = 0x01b4,
73 LCD_SPU_DUMB_CTRL = 0x01b8,
74 LCD_SPU_IOPAD_CONTROL = 0x01bc,
75 LCD_SPU_IRQ_ENA = 0x01c0,
76 LCD_SPU_IRQ_ISR = 0x01c4,
77};
78
79/* For LCD_SPU_ADV_REG */
80enum {
81 ADV_VSYNC_L_OFF = 0xfff << 20,
82 ADV_GRACOLORKEY = 1 << 19,
83 ADV_VIDCOLORKEY = 1 << 18,
84 ADV_HWC32BLEND = 1 << 15,
85 ADV_HWC32ARGB = 1 << 14,
86 ADV_HWC32ENABLE = 1 << 13,
87 ADV_VSYNCOFFEN = 1 << 12,
88 ADV_VSYNC_H_OFF = 0xfff << 0,
89};
90
91enum {
92 CFG_565 = 0,
93 CFG_1555 = 1,
94 CFG_888PACK = 2,
95 CFG_X888 = 3,
96 CFG_8888 = 4,
97 CFG_422PACK = 5,
98 CFG_422 = 6,
99 CFG_420 = 7,
100 CFG_PSEUDO4 = 9,
101 CFG_PSEUDO8 = 10,
102 CFG_SWAPRB = 1 << 4,
103 CFG_SWAPUV = 1 << 3,
104 CFG_SWAPYU = 1 << 2,
105 CFG_YUV2RGB = 1 << 1,
106};
107
108/* For LCD_SPU_DMA_CTRL0 */
109enum {
110 CFG_NOBLENDING = 1 << 31,
111 CFG_GAMMA_ENA = 1 << 30,
112 CFG_CBSH_ENA = 1 << 29,
113 CFG_PALETTE_ENA = 1 << 28,
114 CFG_ARBFAST_ENA = 1 << 27,
115 CFG_HWC_1BITMOD = 1 << 26,
116 CFG_HWC_1BITENA = 1 << 25,
117 CFG_HWC_ENA = 1 << 24,
118 CFG_DMAFORMAT = 0xf << 20,
119#define CFG_DMA_FMT(x) ((x) << 20)
120 CFG_GRAFORMAT = 0xf << 16,
121#define CFG_GRA_FMT(x) ((x) << 16)
122#define CFG_GRA_MOD(x) ((x) << 8)
123 CFG_GRA_FTOGGLE = 1 << 15,
124 CFG_GRA_HSMOOTH = 1 << 14,
125 CFG_GRA_TSTMODE = 1 << 13,
126 CFG_GRA_ENA = 1 << 8,
127#define CFG_DMA_MOD(x) ((x) << 0)
128 CFG_DMA_FTOGGLE = 1 << 7,
129 CFG_DMA_HSMOOTH = 1 << 6,
130 CFG_DMA_TSTMODE = 1 << 5,
131 CFG_DMA_ENA = 1 << 0,
132};
133
134enum {
135 CKMODE_DISABLE = 0,
136 CKMODE_Y = 1,
137 CKMODE_U = 2,
138 CKMODE_RGB = 3,
139 CKMODE_V = 4,
140 CKMODE_R = 5,
141 CKMODE_G = 6,
142 CKMODE_B = 7,
143};
144
145/* For LCD_SPU_DMA_CTRL1 */
146enum {
147 CFG_FRAME_TRIG = 1 << 31,
148 CFG_VSYNC_INV = 1 << 27,
149 CFG_CKMODE_MASK = 0x7 << 24,
150#define CFG_CKMODE(x) ((x) << 24)
151 CFG_CARRY = 1 << 23,
152 CFG_GATED_CLK = 1 << 21,
153 CFG_PWRDN_ENA = 1 << 20,
154 CFG_DSCALE_MASK = 0x3 << 18,
155 CFG_DSCALE_NONE = 0x0 << 18,
156 CFG_DSCALE_HALF = 0x1 << 18,
157 CFG_DSCALE_QUAR = 0x2 << 18,
158 CFG_ALPHAM_MASK = 0x3 << 16,
159 CFG_ALPHAM_VIDEO = 0x0 << 16,
160 CFG_ALPHAM_GRA = 0x1 << 16,
161 CFG_ALPHAM_CFG = 0x2 << 16,
162 CFG_ALPHA_MASK = 0xff << 8,
163 CFG_PIXCMD_MASK = 0xff,
164};
165
166/* For LCD_SPU_SRAM_CTRL */
167enum {
168 SRAM_READ = 0 << 14,
169 SRAM_WRITE = 2 << 14,
170 SRAM_INIT = 3 << 14,
171 SRAM_HWC32_RAMR = 0xc << 8,
172 SRAM_HWC32_RAMG = 0xd << 8,
173 SRAM_HWC32_RAMB = 0xe << 8,
174 SRAM_HWC32_TRAN = 0xf << 8,
175 SRAM_HWC = 0xf << 8,
176};
177
178/* For LCD_SPU_SRAM_PARA1 */
179enum {
180 CFG_CSB_256x32 = 1 << 15, /* cursor */
181 CFG_CSB_256x24 = 1 << 14, /* palette */
182 CFG_CSB_256x8 = 1 << 13, /* gamma */
183 CFG_PDWN1920x32 = 1 << 8, /* Armada 510: power down vscale ram */
184 CFG_PDWN256x32 = 1 << 7, /* power down cursor */
185 CFG_PDWN256x24 = 1 << 6, /* power down palette */
186 CFG_PDWN256x8 = 1 << 5, /* power down gamma */
187 CFG_PDWNHWC = 1 << 4, /* Armada 510: power down all hwc ram */
188 CFG_PDWN32x32 = 1 << 3, /* power down slave->smart ram */
189 CFG_PDWN16x66 = 1 << 2, /* power down UV fifo */
190 CFG_PDWN32x66 = 1 << 1, /* power down Y fifo */
191 CFG_PDWN64x66 = 1 << 0, /* power down graphic fifo */
192};
193
194/* For LCD_CFG_SCLK_DIV */
195enum {
196 /* Armada 510 */
197 SCLK_510_AXI = 0x0 << 30,
198 SCLK_510_EXTCLK0 = 0x1 << 30,
199 SCLK_510_PLL = 0x2 << 30,
200 SCLK_510_EXTCLK1 = 0x3 << 30,
201 SCLK_510_DIV_CHANGE = 1 << 29,
202 SCLK_510_FRAC_DIV_MASK = 0xfff << 16,
203 SCLK_510_INT_DIV_MASK = 0xffff << 0,
204
205 /* Armada 16x */
206 SCLK_16X_AHB = 0x0 << 28,
207 SCLK_16X_PCLK = 0x1 << 28,
208 SCLK_16X_AXI = 0x4 << 28,
209 SCLK_16X_PLL = 0x8 << 28,
210 SCLK_16X_FRAC_DIV_MASK = 0xfff << 16,
211 SCLK_16X_INT_DIV_MASK = 0xffff << 0,
212};
213
214/* For LCD_SPU_DUMB_CTRL */
215enum {
216 DUMB16_RGB565_0 = 0x0 << 28,
217 DUMB16_RGB565_1 = 0x1 << 28,
218 DUMB18_RGB666_0 = 0x2 << 28,
219 DUMB18_RGB666_1 = 0x3 << 28,
220 DUMB12_RGB444_0 = 0x4 << 28,
221 DUMB12_RGB444_1 = 0x5 << 28,
222 DUMB24_RGB888_0 = 0x6 << 28,
223 DUMB_BLANK = 0x7 << 28,
224 DUMB_MASK = 0xf << 28,
225 CFG_BIAS_OUT = 1 << 8,
226 CFG_REV_RGB = 1 << 7,
227 CFG_INV_CBLANK = 1 << 6,
228 CFG_INV_CSYNC = 1 << 5, /* Normally active high */
229 CFG_INV_HENA = 1 << 4,
230 CFG_INV_VSYNC = 1 << 3, /* Normally active high */
231 CFG_INV_HSYNC = 1 << 2, /* Normally active high */
232 CFG_INV_PCLK = 1 << 1,
233 CFG_DUMB_ENA = 1 << 0,
234};
235
236/* For LCD_SPU_IOPAD_CONTROL */
237enum {
238 CFG_VSCALE_LN_EN = 3 << 18,
239 CFG_GRA_VM_ENA = 1 << 15,
240 CFG_DMA_VM_ENA = 1 << 13,
241 CFG_CMD_VM_ENA = 1 << 11,
242 CFG_CSC_MASK = 3 << 8,
243 CFG_CSC_YUV_CCIR709 = 1 << 9,
244 CFG_CSC_YUV_CCIR601 = 0 << 9,
245 CFG_CSC_RGB_STUDIO = 1 << 8,
246 CFG_CSC_RGB_COMPUTER = 0 << 8,
247 CFG_IOPAD_MASK = 0xf << 0,
248 CFG_IOPAD_DUMB24 = 0x0 << 0,
249 CFG_IOPAD_DUMB18SPI = 0x1 << 0,
250 CFG_IOPAD_DUMB18GPIO = 0x2 << 0,
251 CFG_IOPAD_DUMB16SPI = 0x3 << 0,
252 CFG_IOPAD_DUMB16GPIO = 0x4 << 0,
253 CFG_IOPAD_DUMB12GPIO = 0x5 << 0,
254 CFG_IOPAD_SMART18 = 0x6 << 0,
255 CFG_IOPAD_SMART16 = 0x7 << 0,
256 CFG_IOPAD_SMART8 = 0x8 << 0,
257};
258
259#define IOPAD_DUMB24 0x0
260
261/* For LCD_SPU_IRQ_ENA */
262enum {
263 DMA_FRAME_IRQ0_ENA = 1 << 31,
264 DMA_FRAME_IRQ1_ENA = 1 << 30,
265 DMA_FRAME_IRQ_ENA = DMA_FRAME_IRQ0_ENA | DMA_FRAME_IRQ1_ENA,
266 DMA_FF_UNDERFLOW_ENA = 1 << 29,
267 GRA_FRAME_IRQ0_ENA = 1 << 27,
268 GRA_FRAME_IRQ1_ENA = 1 << 26,
269 GRA_FRAME_IRQ_ENA = GRA_FRAME_IRQ0_ENA | GRA_FRAME_IRQ1_ENA,
270 GRA_FF_UNDERFLOW_ENA = 1 << 25,
271 VSYNC_IRQ_ENA = 1 << 23,
272 DUMB_FRAMEDONE_ENA = 1 << 22,
273 TWC_FRAMEDONE_ENA = 1 << 21,
274 HWC_FRAMEDONE_ENA = 1 << 20,
275 SLV_IRQ_ENA = 1 << 19,
276 SPI_IRQ_ENA = 1 << 18,
277 PWRDN_IRQ_ENA = 1 << 17,
278 ERR_IRQ_ENA = 1 << 16,
279 CLEAN_SPU_IRQ_ISR = 0xffff,
280};
281
282/* For LCD_SPU_IRQ_ISR */
283enum {
284 DMA_FRAME_IRQ0 = 1 << 31,
285 DMA_FRAME_IRQ1 = 1 << 30,
286 DMA_FRAME_IRQ = DMA_FRAME_IRQ0 | DMA_FRAME_IRQ1,
287 DMA_FF_UNDERFLOW = 1 << 29,
288 GRA_FRAME_IRQ0 = 1 << 27,
289 GRA_FRAME_IRQ1 = 1 << 26,
290 GRA_FRAME_IRQ = GRA_FRAME_IRQ0 | GRA_FRAME_IRQ1,
291 GRA_FF_UNDERFLOW = 1 << 25,
292 VSYNC_IRQ = 1 << 23,
293 DUMB_FRAMEDONE = 1 << 22,
294 TWC_FRAMEDONE = 1 << 21,
295 HWC_FRAMEDONE = 1 << 20,
296 SLV_IRQ = 1 << 19,
297 SPI_IRQ = 1 << 18,
298 PWRDN_IRQ = 1 << 17,
299 ERR_IRQ = 1 << 16,
300 DMA_FRAME_IRQ0_LEVEL = 1 << 15,
301 DMA_FRAME_IRQ1_LEVEL = 1 << 14,
302 DMA_FRAME_CNT_ISR = 3 << 12,
303 GRA_FRAME_IRQ0_LEVEL = 1 << 11,
304 GRA_FRAME_IRQ1_LEVEL = 1 << 10,
305 GRA_FRAME_CNT_ISR = 3 << 8,
306 VSYNC_IRQ_LEVEL = 1 << 7,
307 DUMB_FRAMEDONE_LEVEL = 1 << 6,
308 TWC_FRAMEDONE_LEVEL = 1 << 5,
309 HWC_FRAMEDONE_LEVEL = 1 << 4,
310 SLV_FF_EMPTY = 1 << 3,
311 DMA_FF_ALLEMPTY = 1 << 2,
312 GRA_FF_ALLEMPTY = 1 << 1,
313 PWRDN_IRQ_LEVEL = 1 << 0,
314};
315
316#endif
diff --git a/drivers/gpu/drm/armada/armada_ioctlP.h b/drivers/gpu/drm/armada/armada_ioctlP.h
new file mode 100644
index 000000000000..bd8c4562066c
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_ioctlP.h
@@ -0,0 +1,18 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#ifndef ARMADA_IOCTLP_H
9#define ARMADA_IOCTLP_H
10
11#define ARMADA_IOCTL_PROTO(name)\
12extern int armada_##name##_ioctl(struct drm_device *, void *, struct drm_file *)
13
14ARMADA_IOCTL_PROTO(gem_create);
15ARMADA_IOCTL_PROTO(gem_mmap);
16ARMADA_IOCTL_PROTO(gem_pwrite);
17
18#endif
diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c
new file mode 100644
index 000000000000..d685a5421485
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_output.c
@@ -0,0 +1,158 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#include <drm/drmP.h>
9#include <drm/drm_crtc_helper.h>
10#include <drm/drm_edid.h>
11#include <drm/drm_encoder_slave.h>
12#include "armada_output.h"
13#include "armada_drm.h"
14
15struct armada_connector {
16 struct drm_connector conn;
17 const struct armada_output_type *type;
18};
19
20#define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn)
21
22struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn)
23{
24 struct drm_encoder *enc = conn->encoder;
25
26 return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]);
27}
28
29static enum drm_connector_status armada_drm_connector_detect(
30 struct drm_connector *conn, bool force)
31{
32 struct armada_connector *dconn = drm_to_armada_conn(conn);
33 enum drm_connector_status status = connector_status_disconnected;
34
35 if (dconn->type->detect) {
36 status = dconn->type->detect(conn, force);
37 } else {
38 struct drm_encoder *enc = armada_drm_connector_encoder(conn);
39
40 if (enc)
41 status = encoder_helper_funcs(enc)->detect(enc, conn);
42 }
43
44 return status;
45}
46
47static void armada_drm_connector_destroy(struct drm_connector *conn)
48{
49 struct armada_connector *dconn = drm_to_armada_conn(conn);
50
51 drm_sysfs_connector_remove(conn);
52 drm_connector_cleanup(conn);
53 kfree(dconn);
54}
55
56static int armada_drm_connector_set_property(struct drm_connector *conn,
57 struct drm_property *property, uint64_t value)
58{
59 struct armada_connector *dconn = drm_to_armada_conn(conn);
60
61 if (!dconn->type->set_property)
62 return -EINVAL;
63
64 return dconn->type->set_property(conn, property, value);
65}
66
67static const struct drm_connector_funcs armada_drm_conn_funcs = {
68 .dpms = drm_helper_connector_dpms,
69 .fill_modes = drm_helper_probe_single_connector_modes,
70 .detect = armada_drm_connector_detect,
71 .destroy = armada_drm_connector_destroy,
72 .set_property = armada_drm_connector_set_property,
73};
74
75void armada_drm_encoder_prepare(struct drm_encoder *encoder)
76{
77 encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_OFF);
78}
79
80void armada_drm_encoder_commit(struct drm_encoder *encoder)
81{
82 encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_ON);
83}
84
85bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
86 const struct drm_display_mode *mode, struct drm_display_mode *adjusted)
87{
88 return true;
89}
90
91/* Shouldn't this be a generic helper function? */
92int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
93 struct drm_display_mode *mode)
94{
95 struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
96 int valid = MODE_BAD;
97
98 if (encoder) {
99 struct drm_encoder_slave *slave = to_encoder_slave(encoder);
100
101 valid = slave->slave_funcs->mode_valid(encoder, mode);
102 }
103 return valid;
104}
105
106int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
107 struct drm_property *property, uint64_t value)
108{
109 struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
110 int rc = -EINVAL;
111
112 if (encoder) {
113 struct drm_encoder_slave *slave = to_encoder_slave(encoder);
114
115 rc = slave->slave_funcs->set_property(encoder, conn, property,
116 value);
117 }
118 return rc;
119}
120
121int armada_output_create(struct drm_device *dev,
122 const struct armada_output_type *type, const void *data)
123{
124 struct armada_connector *dconn;
125 int ret;
126
127 dconn = kzalloc(sizeof(*dconn), GFP_KERNEL);
128 if (!dconn)
129 return -ENOMEM;
130
131 dconn->type = type;
132
133 ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs,
134 type->connector_type);
135 if (ret) {
136 DRM_ERROR("unable to init connector\n");
137 goto err_destroy_dconn;
138 }
139
140 ret = type->create(&dconn->conn, data);
141 if (ret)
142 goto err_conn;
143
144 ret = drm_sysfs_connector_add(&dconn->conn);
145 if (ret)
146 goto err_sysfs;
147
148 return 0;
149
150 err_sysfs:
151 if (dconn->conn.encoder)
152 dconn->conn.encoder->funcs->destroy(dconn->conn.encoder);
153 err_conn:
154 drm_connector_cleanup(&dconn->conn);
155 err_destroy_dconn:
156 kfree(dconn);
157 return ret;
158}
diff --git a/drivers/gpu/drm/armada/armada_output.h b/drivers/gpu/drm/armada/armada_output.h
new file mode 100644
index 000000000000..4126d43b5057
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_output.h
@@ -0,0 +1,39 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#ifndef ARMADA_CONNETOR_H
9#define ARMADA_CONNETOR_H
10
11#define encoder_helper_funcs(encoder) \
12 ((struct drm_encoder_helper_funcs *)encoder->helper_private)
13
14struct armada_output_type {
15 int connector_type;
16 enum drm_connector_status (*detect)(struct drm_connector *, bool);
17 int (*create)(struct drm_connector *, const void *);
18 int (*set_property)(struct drm_connector *, struct drm_property *,
19 uint64_t);
20};
21
22struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn);
23
24void armada_drm_encoder_prepare(struct drm_encoder *encoder);
25void armada_drm_encoder_commit(struct drm_encoder *encoder);
26
27bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
28 const struct drm_display_mode *mode, struct drm_display_mode *adj);
29
30int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
31 struct drm_display_mode *mode);
32
33int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
34 struct drm_property *property, uint64_t value);
35
36int armada_output_create(struct drm_device *dev,
37 const struct armada_output_type *type, const void *data);
38
39#endif
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
new file mode 100644
index 000000000000..c5b06fdb459c
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -0,0 +1,477 @@
1/*
2 * Copyright (C) 2012 Russell King
3 * Rewritten from the dovefb driver, and Armada510 manuals.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9#include <drm/drmP.h>
10#include "armada_crtc.h"
11#include "armada_drm.h"
12#include "armada_fb.h"
13#include "armada_gem.h"
14#include "armada_hw.h"
15#include <drm/armada_drm.h>
16#include "armada_ioctlP.h"
17
18struct armada_plane_properties {
19 uint32_t colorkey_yr;
20 uint32_t colorkey_ug;
21 uint32_t colorkey_vb;
22#define K2R(val) (((val) >> 0) & 0xff)
23#define K2G(val) (((val) >> 8) & 0xff)
24#define K2B(val) (((val) >> 16) & 0xff)
25 int16_t brightness;
26 uint16_t contrast;
27 uint16_t saturation;
28 uint32_t colorkey_mode;
29};
30
31struct armada_plane {
32 struct drm_plane base;
33 spinlock_t lock;
34 struct drm_framebuffer *old_fb;
35 uint32_t src_hw;
36 uint32_t dst_hw;
37 uint32_t dst_yx;
38 uint32_t ctrl0;
39 struct {
40 struct armada_vbl_event update;
41 struct armada_regs regs[13];
42 wait_queue_head_t wait;
43 } vbl;
44 struct armada_plane_properties prop;
45};
46#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
47
48
49static void
50armada_ovl_update_attr(struct armada_plane_properties *prop,
51 struct armada_crtc *dcrtc)
52{
53 writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
54 writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U);
55 writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V);
56
57 writel_relaxed(prop->brightness << 16 | prop->contrast,
58 dcrtc->base + LCD_SPU_CONTRAST);
59 /* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */
60 writel_relaxed(prop->saturation << 16,
61 dcrtc->base + LCD_SPU_SATURATION);
62 writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE);
63
64 spin_lock_irq(&dcrtc->irq_lock);
65 armada_updatel(prop->colorkey_mode | CFG_ALPHAM_GRA,
66 CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK,
67 dcrtc->base + LCD_SPU_DMA_CTRL1);
68
69 armada_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG);
70 spin_unlock_irq(&dcrtc->irq_lock);
71}
72
73/* === Plane support === */
74static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)
75{
76 struct armada_plane *dplane = data;
77 struct drm_framebuffer *fb;
78
79 armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
80
81 spin_lock(&dplane->lock);
82 fb = dplane->old_fb;
83 dplane->old_fb = NULL;
84 spin_unlock(&dplane->lock);
85
86 if (fb)
87 armada_drm_queue_unref_work(dcrtc->crtc.dev, fb);
88}
89
90static unsigned armada_limit(int start, unsigned size, unsigned max)
91{
92 int end = start + size;
93 if (end < 0)
94 return 0;
95 if (start < 0)
96 start = 0;
97 return (unsigned)end > max ? max - start : end - start;
98}
99
100static int
101armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
102 struct drm_framebuffer *fb,
103 int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
104 uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
105{
106 struct armada_plane *dplane = drm_to_armada_plane(plane);
107 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
108 uint32_t val, ctrl0;
109 unsigned idx = 0;
110 int ret;
111
112 crtc_w = armada_limit(crtc_x, crtc_w, dcrtc->crtc.mode.hdisplay);
113 crtc_h = armada_limit(crtc_y, crtc_h, dcrtc->crtc.mode.vdisplay);
114 ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
115 CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
116 CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA;
117
118 /* Does the position/size result in nothing to display? */
119 if (crtc_w == 0 || crtc_h == 0) {
120 ctrl0 &= ~CFG_DMA_ENA;
121 }
122
123 /*
124 * FIXME: if the starting point is off screen, we need to
125 * adjust src_x, src_y, src_w, src_h appropriately, and
126 * according to the scale.
127 */
128
129 if (!dcrtc->plane) {
130 dcrtc->plane = plane;
131 armada_ovl_update_attr(&dplane->prop, dcrtc);
132 }
133
134 /* FIXME: overlay on an interlaced display */
135 /* Just updating the position/size? */
136 if (plane->fb == fb && dplane->ctrl0 == ctrl0) {
137 val = (src_h & 0xffff0000) | src_w >> 16;
138 dplane->src_hw = val;
139 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
140 val = crtc_h << 16 | crtc_w;
141 dplane->dst_hw = val;
142 writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
143 val = crtc_y << 16 | crtc_x;
144 dplane->dst_yx = val;
145 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);
146 return 0;
147 } else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) {
148 /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
149 armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66,
150 dcrtc->base + LCD_SPU_SRAM_PARA1);
151 }
152
153 ret = wait_event_timeout(dplane->vbl.wait,
154 list_empty(&dplane->vbl.update.node),
155 HZ/25);
156 if (ret < 0)
157 return ret;
158
159 if (plane->fb != fb) {
160 struct armada_gem_object *obj = drm_fb_obj(fb);
161 uint32_t sy, su, sv;
162
163 /*
164 * Take a reference on the new framebuffer - we want to
165 * hold on to it while the hardware is displaying it.
166 */
167 drm_framebuffer_reference(fb);
168
169 if (plane->fb) {
170 struct drm_framebuffer *older_fb;
171
172 spin_lock_irq(&dplane->lock);
173 older_fb = dplane->old_fb;
174 dplane->old_fb = plane->fb;
175 spin_unlock_irq(&dplane->lock);
176 if (older_fb)
177 armada_drm_queue_unref_work(dcrtc->crtc.dev,
178 older_fb);
179 }
180
181 src_y >>= 16;
182 src_x >>= 16;
183 sy = obj->dev_addr + fb->offsets[0] + src_y * fb->pitches[0] +
184 src_x * fb->bits_per_pixel / 8;
185 su = obj->dev_addr + fb->offsets[1] + src_y * fb->pitches[1] +
186 src_x;
187 sv = obj->dev_addr + fb->offsets[2] + src_y * fb->pitches[2] +
188 src_x;
189
190 armada_reg_queue_set(dplane->vbl.regs, idx, sy,
191 LCD_SPU_DMA_START_ADDR_Y0);
192 armada_reg_queue_set(dplane->vbl.regs, idx, su,
193 LCD_SPU_DMA_START_ADDR_U0);
194 armada_reg_queue_set(dplane->vbl.regs, idx, sv,
195 LCD_SPU_DMA_START_ADDR_V0);
196 armada_reg_queue_set(dplane->vbl.regs, idx, sy,
197 LCD_SPU_DMA_START_ADDR_Y1);
198 armada_reg_queue_set(dplane->vbl.regs, idx, su,
199 LCD_SPU_DMA_START_ADDR_U1);
200 armada_reg_queue_set(dplane->vbl.regs, idx, sv,
201 LCD_SPU_DMA_START_ADDR_V1);
202
203 val = fb->pitches[0] << 16 | fb->pitches[0];
204 armada_reg_queue_set(dplane->vbl.regs, idx, val,
205 LCD_SPU_DMA_PITCH_YC);
206 val = fb->pitches[1] << 16 | fb->pitches[2];
207 armada_reg_queue_set(dplane->vbl.regs, idx, val,
208 LCD_SPU_DMA_PITCH_UV);
209 }
210
211 val = (src_h & 0xffff0000) | src_w >> 16;
212 if (dplane->src_hw != val) {
213 dplane->src_hw = val;
214 armada_reg_queue_set(dplane->vbl.regs, idx, val,
215 LCD_SPU_DMA_HPXL_VLN);
216 }
217 val = crtc_h << 16 | crtc_w;
218 if (dplane->dst_hw != val) {
219 dplane->dst_hw = val;
220 armada_reg_queue_set(dplane->vbl.regs, idx, val,
221 LCD_SPU_DZM_HPXL_VLN);
222 }
223 val = crtc_y << 16 | crtc_x;
224 if (dplane->dst_yx != val) {
225 dplane->dst_yx = val;
226 armada_reg_queue_set(dplane->vbl.regs, idx, val,
227 LCD_SPU_DMA_OVSA_HPXL_VLN);
228 }
229 if (dplane->ctrl0 != ctrl0) {
230 dplane->ctrl0 = ctrl0;
231 armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0,
232 CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE |
233 CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE |
234 CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU |
235 CFG_YUV2RGB) | CFG_DMA_ENA,
236 LCD_SPU_DMA_CTRL0);
237 }
238 if (idx) {
239 armada_reg_queue_end(dplane->vbl.regs, idx);
240 armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update);
241 }
242 return 0;
243}
244
245static int armada_plane_disable(struct drm_plane *plane)
246{
247 struct armada_plane *dplane = drm_to_armada_plane(plane);
248 struct drm_framebuffer *fb;
249 struct armada_crtc *dcrtc;
250
251 if (!dplane->base.crtc)
252 return 0;
253
254 dcrtc = drm_to_armada_crtc(dplane->base.crtc);
255 dcrtc->plane = NULL;
256
257 spin_lock_irq(&dcrtc->irq_lock);
258 armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update);
259 armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
260 dplane->ctrl0 = 0;
261 spin_unlock_irq(&dcrtc->irq_lock);
262
263 /* Power down the Y/U/V FIFOs */
264 armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0,
265 dcrtc->base + LCD_SPU_SRAM_PARA1);
266
267 if (plane->fb)
268 drm_framebuffer_unreference(plane->fb);
269
270 spin_lock_irq(&dplane->lock);
271 fb = dplane->old_fb;
272 dplane->old_fb = NULL;
273 spin_unlock_irq(&dplane->lock);
274 if (fb)
275 drm_framebuffer_unreference(fb);
276
277 return 0;
278}
279
280static void armada_plane_destroy(struct drm_plane *plane)
281{
282 kfree(plane);
283}
284
285static int armada_plane_set_property(struct drm_plane *plane,
286 struct drm_property *property, uint64_t val)
287{
288 struct armada_private *priv = plane->dev->dev_private;
289 struct armada_plane *dplane = drm_to_armada_plane(plane);
290 bool update_attr = false;
291
292 if (property == priv->colorkey_prop) {
293#define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
294 dplane->prop.colorkey_yr = CCC(K2R(val));
295 dplane->prop.colorkey_ug = CCC(K2G(val));
296 dplane->prop.colorkey_vb = CCC(K2B(val));
297#undef CCC
298 update_attr = true;
299 } else if (property == priv->colorkey_min_prop) {
300 dplane->prop.colorkey_yr &= ~0x00ff0000;
301 dplane->prop.colorkey_yr |= K2R(val) << 16;
302 dplane->prop.colorkey_ug &= ~0x00ff0000;
303 dplane->prop.colorkey_ug |= K2G(val) << 16;
304 dplane->prop.colorkey_vb &= ~0x00ff0000;
305 dplane->prop.colorkey_vb |= K2B(val) << 16;
306 update_attr = true;
307 } else if (property == priv->colorkey_max_prop) {
308 dplane->prop.colorkey_yr &= ~0xff000000;
309 dplane->prop.colorkey_yr |= K2R(val) << 24;
310 dplane->prop.colorkey_ug &= ~0xff000000;
311 dplane->prop.colorkey_ug |= K2G(val) << 24;
312 dplane->prop.colorkey_vb &= ~0xff000000;
313 dplane->prop.colorkey_vb |= K2B(val) << 24;
314 update_attr = true;
315 } else if (property == priv->colorkey_val_prop) {
316 dplane->prop.colorkey_yr &= ~0x0000ff00;
317 dplane->prop.colorkey_yr |= K2R(val) << 8;
318 dplane->prop.colorkey_ug &= ~0x0000ff00;
319 dplane->prop.colorkey_ug |= K2G(val) << 8;
320 dplane->prop.colorkey_vb &= ~0x0000ff00;
321 dplane->prop.colorkey_vb |= K2B(val) << 8;
322 update_attr = true;
323 } else if (property == priv->colorkey_alpha_prop) {
324 dplane->prop.colorkey_yr &= ~0x000000ff;
325 dplane->prop.colorkey_yr |= K2R(val);
326 dplane->prop.colorkey_ug &= ~0x000000ff;
327 dplane->prop.colorkey_ug |= K2G(val);
328 dplane->prop.colorkey_vb &= ~0x000000ff;
329 dplane->prop.colorkey_vb |= K2B(val);
330 update_attr = true;
331 } else if (property == priv->colorkey_mode_prop) {
332 dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK;
333 dplane->prop.colorkey_mode |= CFG_CKMODE(val);
334 update_attr = true;
335 } else if (property == priv->brightness_prop) {
336 dplane->prop.brightness = val - 256;
337 update_attr = true;
338 } else if (property == priv->contrast_prop) {
339 dplane->prop.contrast = val;
340 update_attr = true;
341 } else if (property == priv->saturation_prop) {
342 dplane->prop.saturation = val;
343 update_attr = true;
344 }
345
346 if (update_attr && dplane->base.crtc)
347 armada_ovl_update_attr(&dplane->prop,
348 drm_to_armada_crtc(dplane->base.crtc));
349
350 return 0;
351}
352
353static const struct drm_plane_funcs armada_plane_funcs = {
354 .update_plane = armada_plane_update,
355 .disable_plane = armada_plane_disable,
356 .destroy = armada_plane_destroy,
357 .set_property = armada_plane_set_property,
358};
359
360static const uint32_t armada_formats[] = {
361 DRM_FORMAT_UYVY,
362 DRM_FORMAT_YUYV,
363 DRM_FORMAT_YUV420,
364 DRM_FORMAT_YVU420,
365 DRM_FORMAT_YUV422,
366 DRM_FORMAT_YVU422,
367 DRM_FORMAT_VYUY,
368 DRM_FORMAT_YVYU,
369 DRM_FORMAT_ARGB8888,
370 DRM_FORMAT_ABGR8888,
371 DRM_FORMAT_XRGB8888,
372 DRM_FORMAT_XBGR8888,
373 DRM_FORMAT_RGB888,
374 DRM_FORMAT_BGR888,
375 DRM_FORMAT_ARGB1555,
376 DRM_FORMAT_ABGR1555,
377 DRM_FORMAT_RGB565,
378 DRM_FORMAT_BGR565,
379};
380
381static struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = {
382 { CKMODE_DISABLE, "disabled" },
383 { CKMODE_Y, "Y component" },
384 { CKMODE_U, "U component" },
385 { CKMODE_V, "V component" },
386 { CKMODE_RGB, "RGB" },
387 { CKMODE_R, "R component" },
388 { CKMODE_G, "G component" },
389 { CKMODE_B, "B component" },
390};
391
392static int armada_overlay_create_properties(struct drm_device *dev)
393{
394 struct armada_private *priv = dev->dev_private;
395
396 if (priv->colorkey_prop)
397 return 0;
398
399 priv->colorkey_prop = drm_property_create_range(dev, 0,
400 "colorkey", 0, 0xffffff);
401 priv->colorkey_min_prop = drm_property_create_range(dev, 0,
402 "colorkey_min", 0, 0xffffff);
403 priv->colorkey_max_prop = drm_property_create_range(dev, 0,
404 "colorkey_max", 0, 0xffffff);
405 priv->colorkey_val_prop = drm_property_create_range(dev, 0,
406 "colorkey_val", 0, 0xffffff);
407 priv->colorkey_alpha_prop = drm_property_create_range(dev, 0,
408 "colorkey_alpha", 0, 0xffffff);
409 priv->colorkey_mode_prop = drm_property_create_enum(dev, 0,
410 "colorkey_mode",
411 armada_drm_colorkey_enum_list,
412 ARRAY_SIZE(armada_drm_colorkey_enum_list));
413 priv->brightness_prop = drm_property_create_range(dev, 0,
414 "brightness", 0, 256 + 255);
415 priv->contrast_prop = drm_property_create_range(dev, 0,
416 "contrast", 0, 0x7fff);
417 priv->saturation_prop = drm_property_create_range(dev, 0,
418 "saturation", 0, 0x7fff);
419
420 if (!priv->colorkey_prop)
421 return -ENOMEM;
422
423 return 0;
424}
425
426int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
427{
428 struct armada_private *priv = dev->dev_private;
429 struct drm_mode_object *mobj;
430 struct armada_plane *dplane;
431 int ret;
432
433 ret = armada_overlay_create_properties(dev);
434 if (ret)
435 return ret;
436
437 dplane = kzalloc(sizeof(*dplane), GFP_KERNEL);
438 if (!dplane)
439 return -ENOMEM;
440
441 spin_lock_init(&dplane->lock);
442 init_waitqueue_head(&dplane->vbl.wait);
443 armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl,
444 dplane);
445
446 drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs,
447 armada_formats, ARRAY_SIZE(armada_formats), false);
448
449 dplane->prop.colorkey_yr = 0xfefefe00;
450 dplane->prop.colorkey_ug = 0x01010100;
451 dplane->prop.colorkey_vb = 0x01010100;
452 dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB);
453 dplane->prop.brightness = 0;
454 dplane->prop.contrast = 0x4000;
455 dplane->prop.saturation = 0x4000;
456
457 mobj = &dplane->base.base;
458 drm_object_attach_property(mobj, priv->colorkey_prop,
459 0x0101fe);
460 drm_object_attach_property(mobj, priv->colorkey_min_prop,
461 0x0101fe);
462 drm_object_attach_property(mobj, priv->colorkey_max_prop,
463 0x0101fe);
464 drm_object_attach_property(mobj, priv->colorkey_val_prop,
465 0x0101fe);
466 drm_object_attach_property(mobj, priv->colorkey_alpha_prop,
467 0x000000);
468 drm_object_attach_property(mobj, priv->colorkey_mode_prop,
469 CKMODE_RGB);
470 drm_object_attach_property(mobj, priv->brightness_prop, 256);
471 drm_object_attach_property(mobj, priv->contrast_prop,
472 dplane->prop.contrast);
473 drm_object_attach_property(mobj, priv->saturation_prop,
474 dplane->prop.saturation);
475
476 return 0;
477}
diff --git a/drivers/gpu/drm/armada/armada_slave.c b/drivers/gpu/drm/armada/armada_slave.c
new file mode 100644
index 000000000000..00d0facb42f3
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_slave.c
@@ -0,0 +1,139 @@
1/*
2 * Copyright (C) 2012 Russell King
3 * Rewritten from the dovefb driver, and Armada510 manuals.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9#include <drm/drmP.h>
10#include <drm/drm_crtc_helper.h>
11#include <drm/drm_edid.h>
12#include <drm/drm_encoder_slave.h>
13#include "armada_drm.h"
14#include "armada_output.h"
15#include "armada_slave.h"
16
17static int armada_drm_slave_get_modes(struct drm_connector *conn)
18{
19 struct drm_encoder *enc = armada_drm_connector_encoder(conn);
20 int count = 0;
21
22 if (enc) {
23 struct drm_encoder_slave *slave = to_encoder_slave(enc);
24
25 count = slave->slave_funcs->get_modes(enc, conn);
26 }
27
28 return count;
29}
30
31static void armada_drm_slave_destroy(struct drm_encoder *enc)
32{
33 struct drm_encoder_slave *slave = to_encoder_slave(enc);
34 struct i2c_client *client = drm_i2c_encoder_get_client(enc);
35
36 if (slave->slave_funcs)
37 slave->slave_funcs->destroy(enc);
38 if (client)
39 i2c_put_adapter(client->adapter);
40
41 drm_encoder_cleanup(&slave->base);
42 kfree(slave);
43}
44
45static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = {
46 .destroy = armada_drm_slave_destroy,
47};
48
49static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = {
50 .get_modes = armada_drm_slave_get_modes,
51 .mode_valid = armada_drm_slave_encoder_mode_valid,
52 .best_encoder = armada_drm_connector_encoder,
53};
54
55static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = {
56 .dpms = drm_i2c_encoder_dpms,
57 .save = drm_i2c_encoder_save,
58 .restore = drm_i2c_encoder_restore,
59 .mode_fixup = drm_i2c_encoder_mode_fixup,
60 .prepare = drm_i2c_encoder_prepare,
61 .commit = drm_i2c_encoder_commit,
62 .mode_set = drm_i2c_encoder_mode_set,
63 .detect = drm_i2c_encoder_detect,
64};
65
66static int
67armada_drm_conn_slave_create(struct drm_connector *conn, const void *data)
68{
69 const struct armada_drm_slave_config *config = data;
70 struct drm_encoder_slave *slave;
71 struct i2c_adapter *adap;
72 int ret;
73
74 conn->interlace_allowed = config->interlace_allowed;
75 conn->doublescan_allowed = config->doublescan_allowed;
76 conn->polled = config->polled;
77
78 drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs);
79
80 slave = kzalloc(sizeof(*slave), GFP_KERNEL);
81 if (!slave)
82 return -ENOMEM;
83
84 slave->base.possible_crtcs = config->crtcs;
85
86 adap = i2c_get_adapter(config->i2c_adapter_id);
87 if (!adap) {
88 kfree(slave);
89 return -EPROBE_DEFER;
90 }
91
92 ret = drm_encoder_init(conn->dev, &slave->base,
93 &armada_drm_slave_encoder_funcs,
94 DRM_MODE_ENCODER_TMDS);
95 if (ret) {
96 DRM_ERROR("unable to init encoder\n");
97 i2c_put_adapter(adap);
98 kfree(slave);
99 return ret;
100 }
101
102 ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info);
103 i2c_put_adapter(adap);
104 if (ret) {
105 DRM_ERROR("unable to init encoder slave\n");
106 armada_drm_slave_destroy(&slave->base);
107 return ret;
108 }
109
110 drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers);
111
112 ret = slave->slave_funcs->create_resources(&slave->base, conn);
113 if (ret) {
114 armada_drm_slave_destroy(&slave->base);
115 return ret;
116 }
117
118 ret = drm_mode_connector_attach_encoder(conn, &slave->base);
119 if (ret) {
120 armada_drm_slave_destroy(&slave->base);
121 return ret;
122 }
123
124 conn->encoder = &slave->base;
125
126 return ret;
127}
128
129static const struct armada_output_type armada_drm_conn_slave = {
130 .connector_type = DRM_MODE_CONNECTOR_HDMIA,
131 .create = armada_drm_conn_slave_create,
132 .set_property = armada_drm_slave_encoder_set_property,
133};
134
135int armada_drm_connector_slave_create(struct drm_device *dev,
136 const struct armada_drm_slave_config *config)
137{
138 return armada_output_create(dev, &armada_drm_conn_slave, config);
139}
diff --git a/drivers/gpu/drm/armada/armada_slave.h b/drivers/gpu/drm/armada/armada_slave.h
new file mode 100644
index 000000000000..bf2374c96fc1
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_slave.h
@@ -0,0 +1,26 @@
1/*
2 * Copyright (C) 2012 Russell King
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#ifndef ARMADA_SLAVE_H
9#define ARMADA_SLAVE_H
10
11#include <linux/i2c.h>
12#include <drm/drmP.h>
13
14struct armada_drm_slave_config {
15 int i2c_adapter_id;
16 uint32_t crtcs;
17 uint8_t polled;
18 bool interlace_allowed;
19 bool doublescan_allowed;
20 struct i2c_board_info info;
21};
22
23int armada_drm_connector_slave_create(struct drm_device *dev,
24 const struct armada_drm_slave_config *);
25
26#endif
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 24f499569a2f..e5e6e9124cbc 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1135,4 +1135,21 @@ extern int drm_format_horz_chroma_subsampling(uint32_t format);
1135extern int drm_format_vert_chroma_subsampling(uint32_t format); 1135extern int drm_format_vert_chroma_subsampling(uint32_t format);
1136extern const char *drm_get_format_name(uint32_t format); 1136extern const char *drm_get_format_name(uint32_t format);
1137 1137
1138/* Helpers */
1139static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev,
1140 uint32_t id)
1141{
1142 struct drm_mode_object *mo;
1143 mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CRTC);
1144 return mo ? obj_to_crtc(mo) : NULL;
1145}
1146
1147static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
1148 uint32_t id)
1149{
1150 struct drm_mode_object *mo;
1151 mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
1152 return mo ? obj_to_encoder(mo) : NULL;
1153}
1154
1138#endif /* __DRM_CRTC_H__ */ 1155#endif /* __DRM_CRTC_H__ */
diff --git a/include/uapi/drm/armada_drm.h b/include/uapi/drm/armada_drm.h
new file mode 100644
index 000000000000..8dec3fdc99c7
--- /dev/null
+++ b/include/uapi/drm/armada_drm.h
@@ -0,0 +1,45 @@
1/*
2 * Copyright (C) 2012 Russell King
3 * With inspiration from the i915 driver
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9#ifndef DRM_ARMADA_IOCTL_H
10#define DRM_ARMADA_IOCTL_H
11
12#define DRM_ARMADA_GEM_CREATE 0x00
13#define DRM_ARMADA_GEM_MMAP 0x02
14#define DRM_ARMADA_GEM_PWRITE 0x03
15
16#define ARMADA_IOCTL(dir, name, str) \
17 DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
18
19struct drm_armada_gem_create {
20 uint32_t handle;
21 uint32_t size;
22};
23#define DRM_IOCTL_ARMADA_GEM_CREATE \
24 ARMADA_IOCTL(IOWR, GEM_CREATE, gem_create)
25
26struct drm_armada_gem_mmap {
27 uint32_t handle;
28 uint32_t pad;
29 uint64_t offset;
30 uint64_t size;
31 uint64_t addr;
32};
33#define DRM_IOCTL_ARMADA_GEM_MMAP \
34 ARMADA_IOCTL(IOWR, GEM_MMAP, gem_mmap)
35
36struct drm_armada_gem_pwrite {
37 uint64_t ptr;
38 uint32_t handle;
39 uint32_t offset;
40 uint32_t size;
41};
42#define DRM_IOCTL_ARMADA_GEM_PWRITE \
43 ARMADA_IOCTL(IOW, GEM_PWRITE, gem_pwrite)
44
45#endif