aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2013-10-22 04:38:18 -0400
committerDave Airlie <airlied@redhat.com>2013-10-22 04:50:08 -0400
commit5e4e3ba997af8c756c23ffaa86652d3a4df5bdfa (patch)
treea6860666ccf18c9add51d2b0f468cecf3fd7b3da /drivers/gpu
parent5bdebb183c9702a8c57a01dff09337be3de337a6 (diff)
parent585b691e2180e1501637050290292f77f5c30c7b (diff)
Merge branch 'drm-tda998x-3.12' of git://ftp.arm.linux.org.uk/~rmk/linux-cubox into drm-next
This adds support for the Armada 510 display subsystem found on the Marvell Dove devices. This IP is re-used across several different Marvell SoCs with various tweaks, and this driver has been structured to allow the other IPs to re-use the bulk of this code; further work in this area is expected from interested parties. This has been extensively tested on the SolidRun Cubox platform and appears to work well there. [airlied: update for api changes merged previous to this]
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/armada/Kconfig24
-rw-r--r--drivers/gpu/drm/armada/Makefile7
-rw-r--r--drivers/gpu/drm/armada/armada_510.c87
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.c1098
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.h83
-rw-r--r--drivers/gpu/drm/armada/armada_debugfs.c183
-rw-r--r--drivers/gpu/drm/armada/armada_drm.h113
-rw-r--r--drivers/gpu/drm/armada/armada_drv.c421
-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.h318
-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--drivers/gpu/drm/i2c/tda998x_drv.c3
23 files changed, 4256 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..87e62dd4431d
--- /dev/null
+++ b/drivers/gpu/drm/armada/Kconfig
@@ -0,0 +1,24 @@
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.
16
17config DRM_ARMADA_TDA1998X
18 bool "Support TDA1998X HDMI output"
19 depends on DRM_ARMADA != n
20 depends on I2C && DRM_I2C_NXP_TDA998X = y
21 default y
22 help
23 Support the TDA1998x HDMI output device found on the Solid-Run
24 CuBox.
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..59948eff6095
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_510.c
@@ -0,0 +1,87 @@
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 .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
84 .init = armada510_init,
85 .crtc_init = armada510_crtc_init,
86 .crtc_compute_clock = armada510_crtc_compute_clock,
87};
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
new file mode 100644
index 000000000000..d8e398275ca8
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -0,0 +1,1098 @@
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, base + LCD_SPU_ADV_REG);
385 }
386
387 if (stat & DUMB_FRAMEDONE && dcrtc->cursor_update) {
388 writel_relaxed(dcrtc->cursor_hw_pos,
389 base + LCD_SPU_HWC_OVSA_HPXL_VLN);
390 writel_relaxed(dcrtc->cursor_hw_sz,
391 base + LCD_SPU_HWC_HPXL_VLN);
392 armada_updatel(CFG_HWC_ENA,
393 CFG_HWC_ENA | CFG_HWC_1BITMOD | CFG_HWC_1BITENA,
394 base + LCD_SPU_DMA_CTRL0);
395 dcrtc->cursor_update = false;
396 armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
397 }
398
399 spin_unlock(&dcrtc->irq_lock);
400
401 if (stat & GRA_FRAME_IRQ) {
402 struct drm_device *dev = dcrtc->crtc.dev;
403
404 spin_lock(&dev->event_lock);
405 if (dcrtc->frame_work)
406 armada_drm_crtc_complete_frame_work(dcrtc);
407 spin_unlock(&dev->event_lock);
408
409 wake_up(&dcrtc->frame_wait);
410 }
411}
412
413/* These are locked by dev->vbl_lock */
414void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
415{
416 if (dcrtc->irq_ena & mask) {
417 dcrtc->irq_ena &= ~mask;
418 writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
419 }
420}
421
422void armada_drm_crtc_enable_irq(struct armada_crtc *dcrtc, u32 mask)
423{
424 if ((dcrtc->irq_ena & mask) != mask) {
425 dcrtc->irq_ena |= mask;
426 writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
427 if (readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR) & mask)
428 writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
429 }
430}
431
432static uint32_t armada_drm_crtc_calculate_csc(struct armada_crtc *dcrtc)
433{
434 struct drm_display_mode *adj = &dcrtc->crtc.mode;
435 uint32_t val = 0;
436
437 if (dcrtc->csc_yuv_mode == CSC_YUV_CCIR709)
438 val |= CFG_CSC_YUV_CCIR709;
439 if (dcrtc->csc_rgb_mode == CSC_RGB_STUDIO)
440 val |= CFG_CSC_RGB_STUDIO;
441
442 /*
443 * In auto mode, set the colorimetry, based upon the HDMI spec.
444 * 1280x720p, 1920x1080p and 1920x1080i use ITU709, others use
445 * ITU601. It may be more appropriate to set this depending on
446 * the source - but what if the graphic frame is YUV and the
447 * video frame is RGB?
448 */
449 if ((adj->hdisplay == 1280 && adj->vdisplay == 720 &&
450 !(adj->flags & DRM_MODE_FLAG_INTERLACE)) ||
451 (adj->hdisplay == 1920 && adj->vdisplay == 1080)) {
452 if (dcrtc->csc_yuv_mode == CSC_AUTO)
453 val |= CFG_CSC_YUV_CCIR709;
454 }
455
456 /*
457 * We assume we're connected to a TV-like device, so the YUV->RGB
458 * conversion should produce a limited range. We should set this
459 * depending on the connectors attached to this CRTC, and what
460 * kind of device they report being connected.
461 */
462 if (dcrtc->csc_rgb_mode == CSC_AUTO)
463 val |= CFG_CSC_RGB_STUDIO;
464
465 return val;
466}
467
468/* The mode_config.mutex will be held for this call */
469static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
470 struct drm_display_mode *mode, struct drm_display_mode *adj,
471 int x, int y, struct drm_framebuffer *old_fb)
472{
473 struct armada_private *priv = crtc->dev->dev_private;
474 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
475 struct armada_regs regs[17];
476 uint32_t lm, rm, tm, bm, val, sclk;
477 unsigned long flags;
478 unsigned i;
479 bool interlaced;
480
481 drm_framebuffer_reference(crtc->fb);
482
483 interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE);
484
485 i = armada_drm_crtc_calc_fb(dcrtc->crtc.fb, x, y, regs, interlaced);
486
487 rm = adj->crtc_hsync_start - adj->crtc_hdisplay;
488 lm = adj->crtc_htotal - adj->crtc_hsync_end;
489 bm = adj->crtc_vsync_start - adj->crtc_vdisplay;
490 tm = adj->crtc_vtotal - adj->crtc_vsync_end;
491
492 DRM_DEBUG_DRIVER("H: %d %d %d %d lm %d rm %d\n",
493 adj->crtc_hdisplay,
494 adj->crtc_hsync_start,
495 adj->crtc_hsync_end,
496 adj->crtc_htotal, lm, rm);
497 DRM_DEBUG_DRIVER("V: %d %d %d %d tm %d bm %d\n",
498 adj->crtc_vdisplay,
499 adj->crtc_vsync_start,
500 adj->crtc_vsync_end,
501 adj->crtc_vtotal, tm, bm);
502
503 /* Wait for pending flips to complete */
504 wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
505
506 drm_vblank_pre_modeset(crtc->dev, dcrtc->num);
507
508 crtc->mode = *adj;
509
510 val = dcrtc->dumb_ctrl & ~CFG_DUMB_ENA;
511 if (val != dcrtc->dumb_ctrl) {
512 dcrtc->dumb_ctrl = val;
513 writel_relaxed(val, dcrtc->base + LCD_SPU_DUMB_CTRL);
514 }
515
516 /* Now compute the divider for real */
517 priv->variant->crtc_compute_clock(dcrtc, adj, &sclk);
518
519 /* Ensure graphic fifo is enabled */
520 armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
521 armada_reg_queue_set(regs, i, sclk, LCD_CFG_SCLK_DIV);
522
523 if (interlaced ^ dcrtc->interlaced) {
524 if (adj->flags & DRM_MODE_FLAG_INTERLACE)
525 drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
526 else
527 drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
528 dcrtc->interlaced = interlaced;
529 }
530
531 spin_lock_irqsave(&dcrtc->irq_lock, flags);
532
533 /* Even interlaced/progressive frame */
534 dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 |
535 adj->crtc_htotal;
536 dcrtc->v[1].spu_v_porch = tm << 16 | bm;
537 val = adj->crtc_hsync_start;
538 dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
539 priv->variant->spu_adv_reg;
540
541 if (interlaced) {
542 /* Odd interlaced frame */
543 dcrtc->v[0].spu_v_h_total = dcrtc->v[1].spu_v_h_total +
544 (1 << 16);
545 dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1;
546 val = adj->crtc_hsync_start - adj->crtc_htotal / 2;
547 dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
548 priv->variant->spu_adv_reg;
549 } else {
550 dcrtc->v[0] = dcrtc->v[1];
551 }
552
553 val = adj->crtc_vdisplay << 16 | adj->crtc_hdisplay;
554
555 armada_reg_queue_set(regs, i, val, LCD_SPU_V_H_ACTIVE);
556 armada_reg_queue_set(regs, i, val, LCD_SPU_GRA_HPXL_VLN);
557 armada_reg_queue_set(regs, i, val, LCD_SPU_GZM_HPXL_VLN);
558 armada_reg_queue_set(regs, i, (lm << 16) | rm, LCD_SPU_H_PORCH);
559 armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_porch, LCD_SPU_V_PORCH);
560 armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
561 LCD_SPUT_V_H_TOTAL);
562
563 if (priv->variant->has_spu_adv_reg) {
564 armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
565 ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
566 ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
567 }
568
569 val = CFG_GRA_ENA | CFG_GRA_HSMOOTH;
570 val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt);
571 val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.fb)->mod);
572
573 if (drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt > CFG_420)
574 val |= CFG_PALETTE_ENA;
575
576 if (interlaced)
577 val |= CFG_GRA_FTOGGLE;
578
579 armada_reg_queue_mod(regs, i, val, CFG_GRAFORMAT |
580 CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
581 CFG_SWAPYU | CFG_YUV2RGB) |
582 CFG_PALETTE_ENA | CFG_GRA_FTOGGLE,
583 LCD_SPU_DMA_CTRL0);
584
585 val = adj->flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0;
586 armada_reg_queue_mod(regs, i, val, CFG_VSYNC_INV, LCD_SPU_DMA_CTRL1);
587
588 val = dcrtc->spu_iopad_ctrl | armada_drm_crtc_calculate_csc(dcrtc);
589 armada_reg_queue_set(regs, i, val, LCD_SPU_IOPAD_CONTROL);
590 armada_reg_queue_end(regs, i);
591
592 armada_drm_crtc_update_regs(dcrtc, regs);
593 spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
594
595 armada_drm_crtc_update(dcrtc);
596
597 drm_vblank_post_modeset(crtc->dev, dcrtc->num);
598 armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
599
600 return 0;
601}
602
603/* The mode_config.mutex will be held for this call */
604static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
605 struct drm_framebuffer *old_fb)
606{
607 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
608 struct armada_regs regs[4];
609 unsigned i;
610
611 i = armada_drm_crtc_calc_fb(crtc->fb, crtc->x, crtc->y, regs,
612 dcrtc->interlaced);
613 armada_reg_queue_end(regs, i);
614
615 /* Wait for pending flips to complete */
616 wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
617
618 /* Take a reference to the new fb as we're using it */
619 drm_framebuffer_reference(crtc->fb);
620
621 /* Update the base in the CRTC */
622 armada_drm_crtc_update_regs(dcrtc, regs);
623
624 /* Drop our previously held reference */
625 armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
626
627 return 0;
628}
629
630static void armada_drm_crtc_load_lut(struct drm_crtc *crtc)
631{
632}
633
634/* The mode_config.mutex will be held for this call */
635static void armada_drm_crtc_disable(struct drm_crtc *crtc)
636{
637 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
638
639 armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
640 armada_drm_crtc_finish_fb(dcrtc, crtc->fb, true);
641
642 /* Power down most RAMs and FIFOs */
643 writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
644 CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
645 CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
646}
647
648static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
649 .dpms = armada_drm_crtc_dpms,
650 .prepare = armada_drm_crtc_prepare,
651 .commit = armada_drm_crtc_commit,
652 .mode_fixup = armada_drm_crtc_mode_fixup,
653 .mode_set = armada_drm_crtc_mode_set,
654 .mode_set_base = armada_drm_crtc_mode_set_base,
655 .load_lut = armada_drm_crtc_load_lut,
656 .disable = armada_drm_crtc_disable,
657};
658
659static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix,
660 unsigned stride, unsigned width, unsigned height)
661{
662 uint32_t addr;
663 unsigned y;
664
665 addr = SRAM_HWC32_RAM1;
666 for (y = 0; y < height; y++) {
667 uint32_t *p = &pix[y * stride];
668 unsigned x;
669
670 for (x = 0; x < width; x++, p++) {
671 uint32_t val = *p;
672
673 val = (val & 0xff00ff00) |
674 (val & 0x000000ff) << 16 |
675 (val & 0x00ff0000) >> 16;
676
677 writel_relaxed(val,
678 base + LCD_SPU_SRAM_WRDAT);
679 writel_relaxed(addr | SRAM_WRITE,
680 base + LCD_SPU_SRAM_CTRL);
681 addr += 1;
682 if ((addr & 0x00ff) == 0)
683 addr += 0xf00;
684 if ((addr & 0x30ff) == 0)
685 addr = SRAM_HWC32_RAM2;
686 }
687 }
688}
689
690static void armada_drm_crtc_cursor_tran(void __iomem *base)
691{
692 unsigned addr;
693
694 for (addr = 0; addr < 256; addr++) {
695 /* write the default value */
696 writel_relaxed(0x55555555, base + LCD_SPU_SRAM_WRDAT);
697 writel_relaxed(addr | SRAM_WRITE | SRAM_HWC32_TRAN,
698 base + LCD_SPU_SRAM_CTRL);
699 }
700}
701
702static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload)
703{
704 uint32_t xoff, xscr, w = dcrtc->cursor_w, s;
705 uint32_t yoff, yscr, h = dcrtc->cursor_h;
706 uint32_t para1;
707
708 /*
709 * Calculate the visible width and height of the cursor,
710 * screen position, and the position in the cursor bitmap.
711 */
712 if (dcrtc->cursor_x < 0) {
713 xoff = -dcrtc->cursor_x;
714 xscr = 0;
715 w -= min(xoff, w);
716 } else if (dcrtc->cursor_x + w > dcrtc->crtc.mode.hdisplay) {
717 xoff = 0;
718 xscr = dcrtc->cursor_x;
719 w = max_t(int, dcrtc->crtc.mode.hdisplay - dcrtc->cursor_x, 0);
720 } else {
721 xoff = 0;
722 xscr = dcrtc->cursor_x;
723 }
724
725 if (dcrtc->cursor_y < 0) {
726 yoff = -dcrtc->cursor_y;
727 yscr = 0;
728 h -= min(yoff, h);
729 } else if (dcrtc->cursor_y + h > dcrtc->crtc.mode.vdisplay) {
730 yoff = 0;
731 yscr = dcrtc->cursor_y;
732 h = max_t(int, dcrtc->crtc.mode.vdisplay - dcrtc->cursor_y, 0);
733 } else {
734 yoff = 0;
735 yscr = dcrtc->cursor_y;
736 }
737
738 /* On interlaced modes, the vertical cursor size must be halved */
739 s = dcrtc->cursor_w;
740 if (dcrtc->interlaced) {
741 s *= 2;
742 yscr /= 2;
743 h /= 2;
744 }
745
746 if (!dcrtc->cursor_obj || !h || !w) {
747 spin_lock_irq(&dcrtc->irq_lock);
748 armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
749 dcrtc->cursor_update = false;
750 armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
751 spin_unlock_irq(&dcrtc->irq_lock);
752 return 0;
753 }
754
755 para1 = readl_relaxed(dcrtc->base + LCD_SPU_SRAM_PARA1);
756 armada_updatel(CFG_CSB_256x32, CFG_CSB_256x32 | CFG_PDWN256x32,
757 dcrtc->base + LCD_SPU_SRAM_PARA1);
758
759 /*
760 * Initialize the transparency if the SRAM was powered down.
761 * We must also reload the cursor data as well.
762 */
763 if (!(para1 & CFG_CSB_256x32)) {
764 armada_drm_crtc_cursor_tran(dcrtc->base);
765 reload = true;
766 }
767
768 if (dcrtc->cursor_hw_sz != (h << 16 | w)) {
769 spin_lock_irq(&dcrtc->irq_lock);
770 armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
771 dcrtc->cursor_update = false;
772 armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
773 spin_unlock_irq(&dcrtc->irq_lock);
774 reload = true;
775 }
776 if (reload) {
777 struct armada_gem_object *obj = dcrtc->cursor_obj;
778 uint32_t *pix;
779 /* Set the top-left corner of the cursor image */
780 pix = obj->addr;
781 pix += yoff * s + xoff;
782 armada_load_cursor_argb(dcrtc->base, pix, s, w, h);
783 }
784
785 /* Reload the cursor position, size and enable in the IRQ handler */
786 spin_lock_irq(&dcrtc->irq_lock);
787 dcrtc->cursor_hw_pos = yscr << 16 | xscr;
788 dcrtc->cursor_hw_sz = h << 16 | w;
789 dcrtc->cursor_update = true;
790 armada_drm_crtc_enable_irq(dcrtc, DUMB_FRAMEDONE_ENA);
791 spin_unlock_irq(&dcrtc->irq_lock);
792
793 return 0;
794}
795
796static void cursor_update(void *data)
797{
798 armada_drm_crtc_cursor_update(data, true);
799}
800
801static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc,
802 struct drm_file *file, uint32_t handle, uint32_t w, uint32_t h)
803{
804 struct drm_device *dev = crtc->dev;
805 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
806 struct armada_private *priv = crtc->dev->dev_private;
807 struct armada_gem_object *obj = NULL;
808 int ret;
809
810 /* If no cursor support, replicate drm's return value */
811 if (!priv->variant->has_spu_adv_reg)
812 return -ENXIO;
813
814 if (handle && w > 0 && h > 0) {
815 /* maximum size is 64x32 or 32x64 */
816 if (w > 64 || h > 64 || (w > 32 && h > 32))
817 return -ENOMEM;
818
819 obj = armada_gem_object_lookup(dev, file, handle);
820 if (!obj)
821 return -ENOENT;
822
823 /* Must be a kernel-mapped object */
824 if (!obj->addr) {
825 drm_gem_object_unreference_unlocked(&obj->obj);
826 return -EINVAL;
827 }
828
829 if (obj->obj.size < w * h * 4) {
830 DRM_ERROR("buffer is too small\n");
831 drm_gem_object_unreference_unlocked(&obj->obj);
832 return -ENOMEM;
833 }
834 }
835
836 mutex_lock(&dev->struct_mutex);
837 if (dcrtc->cursor_obj) {
838 dcrtc->cursor_obj->update = NULL;
839 dcrtc->cursor_obj->update_data = NULL;
840 drm_gem_object_unreference(&dcrtc->cursor_obj->obj);
841 }
842 dcrtc->cursor_obj = obj;
843 dcrtc->cursor_w = w;
844 dcrtc->cursor_h = h;
845 ret = armada_drm_crtc_cursor_update(dcrtc, true);
846 if (obj) {
847 obj->update_data = dcrtc;
848 obj->update = cursor_update;
849 }
850 mutex_unlock(&dev->struct_mutex);
851
852 return ret;
853}
854
855static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
856{
857 struct drm_device *dev = crtc->dev;
858 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
859 struct armada_private *priv = crtc->dev->dev_private;
860 int ret;
861
862 /* If no cursor support, replicate drm's return value */
863 if (!priv->variant->has_spu_adv_reg)
864 return -EFAULT;
865
866 mutex_lock(&dev->struct_mutex);
867 dcrtc->cursor_x = x;
868 dcrtc->cursor_y = y;
869 ret = armada_drm_crtc_cursor_update(dcrtc, false);
870 mutex_unlock(&dev->struct_mutex);
871
872 return ret;
873}
874
875static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
876{
877 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
878 struct armada_private *priv = crtc->dev->dev_private;
879
880 if (dcrtc->cursor_obj)
881 drm_gem_object_unreference(&dcrtc->cursor_obj->obj);
882
883 priv->dcrtc[dcrtc->num] = NULL;
884 drm_crtc_cleanup(&dcrtc->crtc);
885
886 if (!IS_ERR(dcrtc->clk))
887 clk_disable_unprepare(dcrtc->clk);
888
889 kfree(dcrtc);
890}
891
892/*
893 * The mode_config lock is held here, to prevent races between this
894 * and a mode_set.
895 */
896static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
897 struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags)
898{
899 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
900 struct armada_frame_work *work;
901 struct drm_device *dev = crtc->dev;
902 unsigned long flags;
903 unsigned i;
904 int ret;
905
906 /* We don't support changing the pixel format */
907 if (fb->pixel_format != crtc->fb->pixel_format)
908 return -EINVAL;
909
910 work = kmalloc(sizeof(*work), GFP_KERNEL);
911 if (!work)
912 return -ENOMEM;
913
914 work->event = event;
915 work->old_fb = dcrtc->crtc.fb;
916
917 i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs,
918 dcrtc->interlaced);
919 armada_reg_queue_end(work->regs, i);
920
921 /*
922 * Hold the old framebuffer for the work - DRM appears to drop our
923 * reference to the old framebuffer in drm_mode_page_flip_ioctl().
924 */
925 drm_framebuffer_reference(work->old_fb);
926
927 ret = armada_drm_crtc_queue_frame_work(dcrtc, work);
928 if (ret) {
929 /*
930 * Undo our reference above; DRM does not drop the reference
931 * to this object on error, so that's okay.
932 */
933 drm_framebuffer_unreference(work->old_fb);
934 kfree(work);
935 return ret;
936 }
937
938 /*
939 * Don't take a reference on the new framebuffer;
940 * drm_mode_page_flip_ioctl() has already grabbed a reference and
941 * will _not_ drop that reference on successful return from this
942 * function. Simply mark this new framebuffer as the current one.
943 */
944 dcrtc->crtc.fb = fb;
945
946 /*
947 * Finally, if the display is blanked, we won't receive an
948 * interrupt, so complete it now.
949 */
950 if (dpms_blanked(dcrtc->dpms)) {
951 spin_lock_irqsave(&dev->event_lock, flags);
952 if (dcrtc->frame_work)
953 armada_drm_crtc_complete_frame_work(dcrtc);
954 spin_unlock_irqrestore(&dev->event_lock, flags);
955 }
956
957 return 0;
958}
959
960static int
961armada_drm_crtc_set_property(struct drm_crtc *crtc,
962 struct drm_property *property, uint64_t val)
963{
964 struct armada_private *priv = crtc->dev->dev_private;
965 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
966 bool update_csc = false;
967
968 if (property == priv->csc_yuv_prop) {
969 dcrtc->csc_yuv_mode = val;
970 update_csc = true;
971 } else if (property == priv->csc_rgb_prop) {
972 dcrtc->csc_rgb_mode = val;
973 update_csc = true;
974 }
975
976 if (update_csc) {
977 uint32_t val;
978
979 val = dcrtc->spu_iopad_ctrl |
980 armada_drm_crtc_calculate_csc(dcrtc);
981 writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
982 }
983
984 return 0;
985}
986
987static struct drm_crtc_funcs armada_crtc_funcs = {
988 .cursor_set = armada_drm_crtc_cursor_set,
989 .cursor_move = armada_drm_crtc_cursor_move,
990 .destroy = armada_drm_crtc_destroy,
991 .set_config = drm_crtc_helper_set_config,
992 .page_flip = armada_drm_crtc_page_flip,
993 .set_property = armada_drm_crtc_set_property,
994};
995
996static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = {
997 { CSC_AUTO, "Auto" },
998 { CSC_YUV_CCIR601, "CCIR601" },
999 { CSC_YUV_CCIR709, "CCIR709" },
1000};
1001
1002static struct drm_prop_enum_list armada_drm_csc_rgb_enum_list[] = {
1003 { CSC_AUTO, "Auto" },
1004 { CSC_RGB_COMPUTER, "Computer system" },
1005 { CSC_RGB_STUDIO, "Studio" },
1006};
1007
1008static int armada_drm_crtc_create_properties(struct drm_device *dev)
1009{
1010 struct armada_private *priv = dev->dev_private;
1011
1012 if (priv->csc_yuv_prop)
1013 return 0;
1014
1015 priv->csc_yuv_prop = drm_property_create_enum(dev, 0,
1016 "CSC_YUV", armada_drm_csc_yuv_enum_list,
1017 ARRAY_SIZE(armada_drm_csc_yuv_enum_list));
1018 priv->csc_rgb_prop = drm_property_create_enum(dev, 0,
1019 "CSC_RGB", armada_drm_csc_rgb_enum_list,
1020 ARRAY_SIZE(armada_drm_csc_rgb_enum_list));
1021
1022 if (!priv->csc_yuv_prop || !priv->csc_rgb_prop)
1023 return -ENOMEM;
1024
1025 return 0;
1026}
1027
1028int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
1029 struct resource *res)
1030{
1031 struct armada_private *priv = dev->dev_private;
1032 struct armada_crtc *dcrtc;
1033 void __iomem *base;
1034 int ret;
1035
1036 ret = armada_drm_crtc_create_properties(dev);
1037 if (ret)
1038 return ret;
1039
1040 base = devm_request_and_ioremap(dev->dev, res);
1041 if (!base) {
1042 DRM_ERROR("failed to ioremap register\n");
1043 return -ENOMEM;
1044 }
1045
1046 dcrtc = kzalloc(sizeof(*dcrtc), GFP_KERNEL);
1047 if (!dcrtc) {
1048 DRM_ERROR("failed to allocate Armada crtc\n");
1049 return -ENOMEM;
1050 }
1051
1052 dcrtc->base = base;
1053 dcrtc->num = num;
1054 dcrtc->clk = ERR_PTR(-EINVAL);
1055 dcrtc->csc_yuv_mode = CSC_AUTO;
1056 dcrtc->csc_rgb_mode = CSC_AUTO;
1057 dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
1058 dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
1059 spin_lock_init(&dcrtc->irq_lock);
1060 dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR;
1061 INIT_LIST_HEAD(&dcrtc->vbl_list);
1062 init_waitqueue_head(&dcrtc->frame_wait);
1063
1064 /* Initialize some registers which we don't otherwise set */
1065 writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV);
1066 writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_BLANKCOLOR);
1067 writel_relaxed(dcrtc->spu_iopad_ctrl,
1068 dcrtc->base + LCD_SPU_IOPAD_CONTROL);
1069 writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_SRAM_PARA0);
1070 writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
1071 CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
1072 CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
1073 writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1);
1074 writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN);
1075
1076 if (priv->variant->crtc_init) {
1077 ret = priv->variant->crtc_init(dcrtc);
1078 if (ret) {
1079 kfree(dcrtc);
1080 return ret;
1081 }
1082 }
1083
1084 /* Ensure AXI pipeline is enabled */
1085 armada_updatel(CFG_ARBFAST_ENA, 0, dcrtc->base + LCD_SPU_DMA_CTRL0);
1086
1087 priv->dcrtc[dcrtc->num] = dcrtc;
1088
1089 drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs);
1090 drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
1091
1092 drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
1093 dcrtc->csc_yuv_mode);
1094 drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop,
1095 dcrtc->csc_rgb_mode);
1096
1097 return armada_overlay_plane_create(dev, 1 << dcrtc->num);
1098}
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
new file mode 100644
index 000000000000..9c10a07e7492
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -0,0 +1,83 @@
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 bool cursor_update;
48 uint8_t csc_yuv_mode;
49 uint8_t csc_rgb_mode;
50
51 struct drm_plane *plane;
52
53 struct armada_gem_object *cursor_obj;
54 int cursor_x;
55 int cursor_y;
56 uint32_t cursor_hw_pos;
57 uint32_t cursor_hw_sz;
58 uint32_t cursor_w;
59 uint32_t cursor_h;
60
61 int dpms;
62 uint32_t cfg_dumb_ctrl;
63 uint32_t dumb_ctrl;
64 uint32_t spu_iopad_ctrl;
65
66 wait_queue_head_t frame_wait;
67 struct armada_frame_work *frame_work;
68
69 spinlock_t irq_lock;
70 uint32_t irq_ena;
71 struct list_head vbl_list;
72};
73#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
74
75int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *);
76void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
77void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
78void armada_drm_crtc_irq(struct armada_crtc *, u32);
79void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
80void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
81void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
82
83#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..eef09ec9a5ff
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -0,0 +1,113 @@
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 uint32_t spu_adv_reg;
64 int (*init)(struct armada_private *, struct device *);
65 int (*crtc_init)(struct armada_crtc *);
66 int (*crtc_compute_clock)(struct armada_crtc *,
67 const struct drm_display_mode *,
68 uint32_t *);
69};
70
71/* Variant ops */
72extern const struct armada_variant armada510_ops;
73
74struct armada_private {
75 const struct armada_variant *variant;
76 struct work_struct fb_unref_work;
77 DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8);
78 struct drm_fb_helper *fbdev;
79 struct armada_crtc *dcrtc[2];
80 struct drm_mm linear;
81 struct clk *extclk[2];
82 struct drm_property *csc_yuv_prop;
83 struct drm_property *csc_rgb_prop;
84 struct drm_property *colorkey_prop;
85 struct drm_property *colorkey_min_prop;
86 struct drm_property *colorkey_max_prop;
87 struct drm_property *colorkey_val_prop;
88 struct drm_property *colorkey_alpha_prop;
89 struct drm_property *colorkey_mode_prop;
90 struct drm_property *brightness_prop;
91 struct drm_property *contrast_prop;
92 struct drm_property *saturation_prop;
93#ifdef CONFIG_DEBUG_FS
94 struct dentry *de;
95#endif
96};
97
98void __armada_drm_queue_unref_work(struct drm_device *,
99 struct drm_framebuffer *);
100void armada_drm_queue_unref_work(struct drm_device *,
101 struct drm_framebuffer *);
102
103extern const struct drm_mode_config_funcs armada_drm_mode_config_funcs;
104
105int armada_fbdev_init(struct drm_device *);
106void armada_fbdev_fini(struct drm_device *);
107
108int armada_overlay_plane_create(struct drm_device *, unsigned long);
109
110int armada_drm_debugfs_init(struct drm_minor *);
111void armada_drm_debugfs_cleanup(struct drm_minor *);
112
113#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..4f2b28354915
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -0,0 +1,421 @@
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
19#ifdef CONFIG_DRM_ARMADA_TDA1998X
20#include <drm/i2c/tda998x.h>
21#include "armada_slave.h"
22
23static struct tda998x_encoder_params params = {
24 /* With 0x24, there is no translation between vp_out and int_vp
25 FB LCD out Pins VIP Int Vp
26 R:23:16 R:7:0 VPC7:0 7:0 7:0[R]
27 G:15:8 G:15:8 VPB7:0 23:16 23:16[G]
28 B:7:0 B:23:16 VPA7:0 15:8 15:8[B]
29 */
30 .swap_a = 2,
31 .swap_b = 3,
32 .swap_c = 4,
33 .swap_d = 5,
34 .swap_e = 0,
35 .swap_f = 1,
36 .audio_cfg = BIT(2),
37 .audio_frame[1] = 1,
38 .audio_format = AFMT_SPDIF,
39 .audio_sample_rate = 44100,
40};
41
42static const struct armada_drm_slave_config tda19988_config = {
43 .i2c_adapter_id = 0,
44 .crtcs = 1 << 0, /* Only LCD0 at the moment */
45 .polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT,
46 .interlace_allowed = true,
47 .info = {
48 .type = "tda998x",
49 .addr = 0x70,
50 .platform_data = &params,
51 },
52};
53#endif
54
55static void armada_drm_unref_work(struct work_struct *work)
56{
57 struct armada_private *priv =
58 container_of(work, struct armada_private, fb_unref_work);
59 struct drm_framebuffer *fb;
60
61 while (kfifo_get(&priv->fb_unref, &fb))
62 drm_framebuffer_unreference(fb);
63}
64
65/* Must be called with dev->event_lock held */
66void __armada_drm_queue_unref_work(struct drm_device *dev,
67 struct drm_framebuffer *fb)
68{
69 struct armada_private *priv = dev->dev_private;
70
71 /*
72 * Yes, we really must jump through these hoops just to store a
73 * _pointer_ to something into the kfifo. This is utterly insane
74 * and idiotic, because it kfifo requires the _data_ pointed to by
75 * the pointer const, not the pointer itself. Not only that, but
76 * you have to pass a pointer _to_ the pointer you want stored.
77 */
78 const struct drm_framebuffer *silly_api_alert = fb;
79 WARN_ON(!kfifo_put(&priv->fb_unref, &silly_api_alert));
80 schedule_work(&priv->fb_unref_work);
81}
82
83void armada_drm_queue_unref_work(struct drm_device *dev,
84 struct drm_framebuffer *fb)
85{
86 unsigned long flags;
87
88 spin_lock_irqsave(&dev->event_lock, flags);
89 __armada_drm_queue_unref_work(dev, fb);
90 spin_unlock_irqrestore(&dev->event_lock, flags);
91}
92
93static int armada_drm_load(struct drm_device *dev, unsigned long flags)
94{
95 const struct platform_device_id *id;
96 struct armada_private *priv;
97 struct resource *res[ARRAY_SIZE(priv->dcrtc)];
98 struct resource *mem = NULL;
99 int ret, n, i;
100
101 memset(res, 0, sizeof(res));
102
103 for (n = i = 0; ; n++) {
104 struct resource *r = platform_get_resource(dev->platformdev,
105 IORESOURCE_MEM, n);
106 if (!r)
107 break;
108
109 /* Resources above 64K are graphics memory */
110 if (resource_size(r) > SZ_64K)
111 mem = r;
112 else if (i < ARRAY_SIZE(priv->dcrtc))
113 res[i++] = r;
114 else
115 return -EINVAL;
116 }
117
118 if (!res[0] || !mem)
119 return -ENXIO;
120
121 if (!devm_request_mem_region(dev->dev, mem->start,
122 resource_size(mem), "armada-drm"))
123 return -EBUSY;
124
125 priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
126 if (!priv) {
127 DRM_ERROR("failed to allocate private\n");
128 return -ENOMEM;
129 }
130
131 dev->dev_private = priv;
132
133 /* Get the implementation specific driver data. */
134 id = platform_get_device_id(dev->platformdev);
135 if (!id)
136 return -ENXIO;
137
138 priv->variant = (struct armada_variant *)id->driver_data;
139
140 ret = priv->variant->init(priv, dev->dev);
141 if (ret)
142 return ret;
143
144 INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
145 INIT_KFIFO(priv->fb_unref);
146
147 /* Mode setting support */
148 drm_mode_config_init(dev);
149 dev->mode_config.min_width = 320;
150 dev->mode_config.min_height = 200;
151
152 /*
153 * With vscale enabled, the maximum width is 1920 due to the
154 * 1920 by 3 lines RAM
155 */
156 dev->mode_config.max_width = 1920;
157 dev->mode_config.max_height = 2048;
158
159 dev->mode_config.preferred_depth = 24;
160 dev->mode_config.funcs = &armada_drm_mode_config_funcs;
161 drm_mm_init(&priv->linear, mem->start, resource_size(mem));
162
163 /* Create all LCD controllers */
164 for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
165 if (!res[n])
166 break;
167
168 ret = armada_drm_crtc_create(dev, n, res[n]);
169 if (ret)
170 goto err_kms;
171 }
172
173#ifdef CONFIG_DRM_ARMADA_TDA1998X
174 ret = armada_drm_connector_slave_create(dev, &tda19988_config);
175 if (ret)
176 goto err_kms;
177#endif
178
179 ret = drm_vblank_init(dev, n);
180 if (ret)
181 goto err_kms;
182
183 ret = drm_irq_install(dev);
184 if (ret)
185 goto err_kms;
186
187 dev->vblank_disable_allowed = 1;
188
189 ret = armada_fbdev_init(dev);
190 if (ret)
191 goto err_irq;
192
193 drm_kms_helper_poll_init(dev);
194
195 return 0;
196
197 err_irq:
198 drm_irq_uninstall(dev);
199 err_kms:
200 drm_mode_config_cleanup(dev);
201 drm_mm_takedown(&priv->linear);
202 flush_work(&priv->fb_unref_work);
203
204 return ret;
205}
206
207static int armada_drm_unload(struct drm_device *dev)
208{
209 struct armada_private *priv = dev->dev_private;
210
211 drm_kms_helper_poll_fini(dev);
212 armada_fbdev_fini(dev);
213 drm_irq_uninstall(dev);
214 drm_mode_config_cleanup(dev);
215 drm_mm_takedown(&priv->linear);
216 flush_work(&priv->fb_unref_work);
217 dev->dev_private = NULL;
218
219 return 0;
220}
221
222void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
223 struct armada_vbl_event *evt)
224{
225 unsigned long flags;
226
227 spin_lock_irqsave(&dcrtc->irq_lock, flags);
228 if (list_empty(&evt->node)) {
229 list_add_tail(&evt->node, &dcrtc->vbl_list);
230
231 drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
232 }
233 spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
234}
235
236void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
237 struct armada_vbl_event *evt)
238{
239 if (!list_empty(&evt->node)) {
240 list_del_init(&evt->node);
241 drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
242 }
243}
244
245void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *dcrtc,
246 struct armada_vbl_event *evt)
247{
248 unsigned long flags;
249
250 spin_lock_irqsave(&dcrtc->irq_lock, flags);
251 armada_drm_vbl_event_remove(dcrtc, evt);
252 spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
253}
254
255/* These are called under the vbl_lock. */
256static int armada_drm_enable_vblank(struct drm_device *dev, int crtc)
257{
258 struct armada_private *priv = dev->dev_private;
259 armada_drm_crtc_enable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
260 return 0;
261}
262
263static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
264{
265 struct armada_private *priv = dev->dev_private;
266 armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
267}
268
269static irqreturn_t armada_drm_irq_handler(int irq, void *arg)
270{
271 struct drm_device *dev = arg;
272 struct armada_private *priv = dev->dev_private;
273 struct armada_crtc *dcrtc = priv->dcrtc[0];
274 uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
275 irqreturn_t handled = IRQ_NONE;
276
277 /*
278 * This is rediculous - rather than writing bits to clear, we
279 * have to set the actual status register value. This is racy.
280 */
281 writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
282
283 /* Mask out those interrupts we haven't enabled */
284 v = stat & dcrtc->irq_ena;
285
286 if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
287 armada_drm_crtc_irq(dcrtc, stat);
288 handled = IRQ_HANDLED;
289 }
290
291 return handled;
292}
293
294static int armada_drm_irq_postinstall(struct drm_device *dev)
295{
296 struct armada_private *priv = dev->dev_private;
297 struct armada_crtc *dcrtc = priv->dcrtc[0];
298
299 spin_lock_irq(&dev->vbl_lock);
300 writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
301 writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
302 spin_unlock_irq(&dev->vbl_lock);
303
304 return 0;
305}
306
307static void armada_drm_irq_uninstall(struct drm_device *dev)
308{
309 struct armada_private *priv = dev->dev_private;
310 struct armada_crtc *dcrtc = priv->dcrtc[0];
311
312 writel(0, dcrtc->base + LCD_SPU_IRQ_ENA);
313}
314
315static struct drm_ioctl_desc armada_ioctls[] = {
316 DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,
317 DRM_UNLOCKED),
318 DRM_IOCTL_DEF_DRV(ARMADA_GEM_MMAP, armada_gem_mmap_ioctl,
319 DRM_UNLOCKED),
320 DRM_IOCTL_DEF_DRV(ARMADA_GEM_PWRITE, armada_gem_pwrite_ioctl,
321 DRM_UNLOCKED),
322};
323
324static const struct file_operations armada_drm_fops = {
325 .owner = THIS_MODULE,
326 .llseek = no_llseek,
327 .read = drm_read,
328 .poll = drm_poll,
329 .unlocked_ioctl = drm_ioctl,
330 .mmap = drm_gem_mmap,
331 .open = drm_open,
332 .release = drm_release,
333};
334
335static struct drm_driver armada_drm_driver = {
336 .load = armada_drm_load,
337 .open = NULL,
338 .preclose = NULL,
339 .postclose = NULL,
340 .lastclose = NULL,
341 .unload = armada_drm_unload,
342 .get_vblank_counter = drm_vblank_count,
343 .enable_vblank = armada_drm_enable_vblank,
344 .disable_vblank = armada_drm_disable_vblank,
345 .irq_handler = armada_drm_irq_handler,
346 .irq_postinstall = armada_drm_irq_postinstall,
347 .irq_uninstall = armada_drm_irq_uninstall,
348#ifdef CONFIG_DEBUG_FS
349 .debugfs_init = armada_drm_debugfs_init,
350 .debugfs_cleanup = armada_drm_debugfs_cleanup,
351#endif
352 .gem_free_object = armada_gem_free_object,
353 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
354 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
355 .gem_prime_export = armada_gem_prime_export,
356 .gem_prime_import = armada_gem_prime_import,
357 .dumb_create = armada_gem_dumb_create,
358 .dumb_map_offset = armada_gem_dumb_map_offset,
359 .dumb_destroy = armada_gem_dumb_destroy,
360 .gem_vm_ops = &armada_gem_vm_ops,
361 .major = 1,
362 .minor = 0,
363 .name = "armada-drm",
364 .desc = "Armada SoC DRM",
365 .date = "20120730",
366 .driver_features = DRIVER_GEM | DRIVER_MODESET |
367 DRIVER_HAVE_IRQ | DRIVER_PRIME,
368 .ioctls = armada_ioctls,
369 .fops = &armada_drm_fops,
370};
371
372static int armada_drm_probe(struct platform_device *pdev)
373{
374 return drm_platform_init(&armada_drm_driver, pdev);
375}
376
377static int armada_drm_remove(struct platform_device *pdev)
378{
379 drm_platform_exit(&armada_drm_driver, pdev);
380 return 0;
381}
382
383static const struct platform_device_id armada_drm_platform_ids[] = {
384 {
385 .name = "armada-drm",
386 .driver_data = (unsigned long)&armada510_ops,
387 }, {
388 .name = "armada-510-drm",
389 .driver_data = (unsigned long)&armada510_ops,
390 },
391 { },
392};
393MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids);
394
395static struct platform_driver armada_drm_platform_driver = {
396 .probe = armada_drm_probe,
397 .remove = armada_drm_remove,
398 .driver = {
399 .name = "armada-drm",
400 .owner = THIS_MODULE,
401 },
402 .id_table = armada_drm_platform_ids,
403};
404
405static int __init armada_drm_init(void)
406{
407 armada_drm_driver.num_ioctls = DRM_ARRAY_SIZE(armada_ioctls);
408 return platform_driver_register(&armada_drm_platform_driver);
409}
410module_init(armada_drm_init);
411
412static void __exit armada_drm_exit(void)
413{
414 platform_driver_unregister(&armada_drm_platform_driver);
415}
416module_exit(armada_drm_exit);
417
418MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
419MODULE_DESCRIPTION("Armada DRM Driver");
420MODULE_LICENSE("GPL");
421MODULE_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..27319a8335e2
--- /dev/null
+++ b/drivers/gpu/drm/armada/armada_hw.h
@@ -0,0 +1,318 @@
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_RAM1 = 0xc << 8,
172 SRAM_HWC32_RAM2 = 0xd << 8,
173 SRAM_HWC32_RAMR = SRAM_HWC32_RAM1,
174 SRAM_HWC32_RAMG = SRAM_HWC32_RAM2,
175 SRAM_HWC32_RAMB = 0xe << 8,
176 SRAM_HWC32_TRAN = 0xf << 8,
177 SRAM_HWC = 0xf << 8,
178};
179
180/* For LCD_SPU_SRAM_PARA1 */
181enum {
182 CFG_CSB_256x32 = 1 << 15, /* cursor */
183 CFG_CSB_256x24 = 1 << 14, /* palette */
184 CFG_CSB_256x8 = 1 << 13, /* gamma */
185 CFG_PDWN1920x32 = 1 << 8, /* Armada 510: power down vscale ram */
186 CFG_PDWN256x32 = 1 << 7, /* power down cursor */
187 CFG_PDWN256x24 = 1 << 6, /* power down palette */
188 CFG_PDWN256x8 = 1 << 5, /* power down gamma */
189 CFG_PDWNHWC = 1 << 4, /* Armada 510: power down all hwc ram */
190 CFG_PDWN32x32 = 1 << 3, /* power down slave->smart ram */
191 CFG_PDWN16x66 = 1 << 2, /* power down UV fifo */
192 CFG_PDWN32x66 = 1 << 1, /* power down Y fifo */
193 CFG_PDWN64x66 = 1 << 0, /* power down graphic fifo */
194};
195
196/* For LCD_CFG_SCLK_DIV */
197enum {
198 /* Armada 510 */
199 SCLK_510_AXI = 0x0 << 30,
200 SCLK_510_EXTCLK0 = 0x1 << 30,
201 SCLK_510_PLL = 0x2 << 30,
202 SCLK_510_EXTCLK1 = 0x3 << 30,
203 SCLK_510_DIV_CHANGE = 1 << 29,
204 SCLK_510_FRAC_DIV_MASK = 0xfff << 16,
205 SCLK_510_INT_DIV_MASK = 0xffff << 0,
206
207 /* Armada 16x */
208 SCLK_16X_AHB = 0x0 << 28,
209 SCLK_16X_PCLK = 0x1 << 28,
210 SCLK_16X_AXI = 0x4 << 28,
211 SCLK_16X_PLL = 0x8 << 28,
212 SCLK_16X_FRAC_DIV_MASK = 0xfff << 16,
213 SCLK_16X_INT_DIV_MASK = 0xffff << 0,
214};
215
216/* For LCD_SPU_DUMB_CTRL */
217enum {
218 DUMB16_RGB565_0 = 0x0 << 28,
219 DUMB16_RGB565_1 = 0x1 << 28,
220 DUMB18_RGB666_0 = 0x2 << 28,
221 DUMB18_RGB666_1 = 0x3 << 28,
222 DUMB12_RGB444_0 = 0x4 << 28,
223 DUMB12_RGB444_1 = 0x5 << 28,
224 DUMB24_RGB888_0 = 0x6 << 28,
225 DUMB_BLANK = 0x7 << 28,
226 DUMB_MASK = 0xf << 28,
227 CFG_BIAS_OUT = 1 << 8,
228 CFG_REV_RGB = 1 << 7,
229 CFG_INV_CBLANK = 1 << 6,
230 CFG_INV_CSYNC = 1 << 5, /* Normally active high */
231 CFG_INV_HENA = 1 << 4,
232 CFG_INV_VSYNC = 1 << 3, /* Normally active high */
233 CFG_INV_HSYNC = 1 << 2, /* Normally active high */
234 CFG_INV_PCLK = 1 << 1,
235 CFG_DUMB_ENA = 1 << 0,
236};
237
238/* For LCD_SPU_IOPAD_CONTROL */
239enum {
240 CFG_VSCALE_LN_EN = 3 << 18,
241 CFG_GRA_VM_ENA = 1 << 15,
242 CFG_DMA_VM_ENA = 1 << 13,
243 CFG_CMD_VM_ENA = 1 << 11,
244 CFG_CSC_MASK = 3 << 8,
245 CFG_CSC_YUV_CCIR709 = 1 << 9,
246 CFG_CSC_YUV_CCIR601 = 0 << 9,
247 CFG_CSC_RGB_STUDIO = 1 << 8,
248 CFG_CSC_RGB_COMPUTER = 0 << 8,
249 CFG_IOPAD_MASK = 0xf << 0,
250 CFG_IOPAD_DUMB24 = 0x0 << 0,
251 CFG_IOPAD_DUMB18SPI = 0x1 << 0,
252 CFG_IOPAD_DUMB18GPIO = 0x2 << 0,
253 CFG_IOPAD_DUMB16SPI = 0x3 << 0,
254 CFG_IOPAD_DUMB16GPIO = 0x4 << 0,
255 CFG_IOPAD_DUMB12GPIO = 0x5 << 0,
256 CFG_IOPAD_SMART18 = 0x6 << 0,
257 CFG_IOPAD_SMART16 = 0x7 << 0,
258 CFG_IOPAD_SMART8 = 0x8 << 0,
259};
260
261#define IOPAD_DUMB24 0x0
262
263/* For LCD_SPU_IRQ_ENA */
264enum {
265 DMA_FRAME_IRQ0_ENA = 1 << 31,
266 DMA_FRAME_IRQ1_ENA = 1 << 30,
267 DMA_FRAME_IRQ_ENA = DMA_FRAME_IRQ0_ENA | DMA_FRAME_IRQ1_ENA,
268 DMA_FF_UNDERFLOW_ENA = 1 << 29,
269 GRA_FRAME_IRQ0_ENA = 1 << 27,
270 GRA_FRAME_IRQ1_ENA = 1 << 26,
271 GRA_FRAME_IRQ_ENA = GRA_FRAME_IRQ0_ENA | GRA_FRAME_IRQ1_ENA,
272 GRA_FF_UNDERFLOW_ENA = 1 << 25,
273 VSYNC_IRQ_ENA = 1 << 23,
274 DUMB_FRAMEDONE_ENA = 1 << 22,
275 TWC_FRAMEDONE_ENA = 1 << 21,
276 HWC_FRAMEDONE_ENA = 1 << 20,
277 SLV_IRQ_ENA = 1 << 19,
278 SPI_IRQ_ENA = 1 << 18,
279 PWRDN_IRQ_ENA = 1 << 17,
280 ERR_IRQ_ENA = 1 << 16,
281 CLEAN_SPU_IRQ_ISR = 0xffff,
282};
283
284/* For LCD_SPU_IRQ_ISR */
285enum {
286 DMA_FRAME_IRQ0 = 1 << 31,
287 DMA_FRAME_IRQ1 = 1 << 30,
288 DMA_FRAME_IRQ = DMA_FRAME_IRQ0 | DMA_FRAME_IRQ1,
289 DMA_FF_UNDERFLOW = 1 << 29,
290 GRA_FRAME_IRQ0 = 1 << 27,
291 GRA_FRAME_IRQ1 = 1 << 26,
292 GRA_FRAME_IRQ = GRA_FRAME_IRQ0 | GRA_FRAME_IRQ1,
293 GRA_FF_UNDERFLOW = 1 << 25,
294 VSYNC_IRQ = 1 << 23,
295 DUMB_FRAMEDONE = 1 << 22,
296 TWC_FRAMEDONE = 1 << 21,
297 HWC_FRAMEDONE = 1 << 20,
298 SLV_IRQ = 1 << 19,
299 SPI_IRQ = 1 << 18,
300 PWRDN_IRQ = 1 << 17,
301 ERR_IRQ = 1 << 16,
302 DMA_FRAME_IRQ0_LEVEL = 1 << 15,
303 DMA_FRAME_IRQ1_LEVEL = 1 << 14,
304 DMA_FRAME_CNT_ISR = 3 << 12,
305 GRA_FRAME_IRQ0_LEVEL = 1 << 11,
306 GRA_FRAME_IRQ1_LEVEL = 1 << 10,
307 GRA_FRAME_CNT_ISR = 3 << 8,
308 VSYNC_IRQ_LEVEL = 1 << 7,
309 DUMB_FRAMEDONE_LEVEL = 1 << 6,
310 TWC_FRAMEDONE_LEVEL = 1 << 5,
311 HWC_FRAMEDONE_LEVEL = 1 << 4,
312 SLV_FF_EMPTY = 1 << 3,
313 DMA_FF_ALLEMPTY = 1 << 2,
314 GRA_FF_ALLEMPTY = 1 << 1,
315 PWRDN_IRQ_LEVEL = 1 << 0,
316};
317
318#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/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 60e84043aa34..400b0c4a10fb 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -17,6 +17,7 @@
17 17
18 18
19 19
20#include <linux/hdmi.h>
20#include <linux/module.h> 21#include <linux/module.h>
21 22
22#include <drm/drmP.h> 23#include <drm/drmP.h>
@@ -549,6 +550,8 @@ tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode)
549 buf[HB(0)] = 0x82; 550 buf[HB(0)] = 0x82;
550 buf[HB(1)] = 0x02; 551 buf[HB(1)] = 0x02;
551 buf[HB(2)] = 13; 552 buf[HB(2)] = 13;
553 buf[PB(1)] = HDMI_SCAN_MODE_UNDERSCAN;
554 buf[PB(3)] = HDMI_QUANTIZATION_RANGE_FULL << 2;
552 buf[PB(4)] = drm_match_cea_mode(mode); 555 buf[PB(4)] = drm_match_cea_mode(mode);
553 556
554 tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf, 557 tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf,