diff options
author | Dave Airlie <airlied@redhat.com> | 2013-10-22 04:38:18 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2013-10-22 04:50:08 -0400 |
commit | 5e4e3ba997af8c756c23ffaa86652d3a4df5bdfa (patch) | |
tree | a6860666ccf18c9add51d2b0f468cecf3fd7b3da /drivers/gpu | |
parent | 5bdebb183c9702a8c57a01dff09337be3de337a6 (diff) | |
parent | 585b691e2180e1501637050290292f77f5c30c7b (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')
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 | ||
226 | source "drivers/gpu/drm/cirrus/Kconfig" | 226 | source "drivers/gpu/drm/cirrus/Kconfig" |
227 | 227 | ||
228 | source "drivers/gpu/drm/armada/Kconfig" | ||
229 | |||
228 | source "drivers/gpu/drm/rcar-du/Kconfig" | 230 | source "drivers/gpu/drm/rcar-du/Kconfig" |
229 | 231 | ||
230 | source "drivers/gpu/drm/shmobile/Kconfig" | 232 | source "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/ | |||
49 | obj-$(CONFIG_DRM_GMA500) += gma500/ | 49 | obj-$(CONFIG_DRM_GMA500) += gma500/ |
50 | obj-$(CONFIG_DRM_UDL) += udl/ | 50 | obj-$(CONFIG_DRM_UDL) += udl/ |
51 | obj-$(CONFIG_DRM_AST) += ast/ | 51 | obj-$(CONFIG_DRM_AST) += ast/ |
52 | obj-$(CONFIG_DRM_ARMADA) += armada/ | ||
52 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ | 53 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ |
53 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ | 54 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ |
54 | obj-$(CONFIG_DRM_OMAP) += omapdrm/ | 55 | obj-$(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 @@ | |||
1 | config 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 | |||
17 | config 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 @@ | |||
1 | armada-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 | ||
4 | armada-y += armada_510.o | ||
5 | armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o | ||
6 | |||
7 | obj-$(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 | |||
18 | static 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 | |||
28 | static 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 | */ | ||
45 | static 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 | |||
81 | const 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 | |||
18 | struct armada_frame_work { | ||
19 | struct drm_pending_vblank_event *event; | ||
20 | struct armada_regs regs[4]; | ||
21 | struct drm_framebuffer *old_fb; | ||
22 | }; | ||
23 | |||
24 | enum 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 | |||
86 | void | ||
87 | armada_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 | |||
103 | static 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 | |||
145 | static 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 | |||
172 | static 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 | |||
198 | static 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 | |||
217 | static 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 | |||
252 | static 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 | |||
269 | void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b, | ||
270 | int idx) | ||
271 | { | ||
272 | } | ||
273 | |||
274 | void 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 */ | ||
280 | static 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 | */ | ||
299 | static 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 */ | ||
321 | static 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 */ | ||
332 | static 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 | |||
352 | void 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 */ | ||
414 | void 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 | |||
422 | void 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 | |||
432 | static 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 */ | ||
469 | static 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 */ | ||
604 | static 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 | |||
630 | static void armada_drm_crtc_load_lut(struct drm_crtc *crtc) | ||
631 | { | ||
632 | } | ||
633 | |||
634 | /* The mode_config.mutex will be held for this call */ | ||
635 | static 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 | |||
648 | static 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 | |||
659 | static 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 | |||
690 | static 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 | |||
702 | static 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 | |||
796 | static void cursor_update(void *data) | ||
797 | { | ||
798 | armada_drm_crtc_cursor_update(data, true); | ||
799 | } | ||
800 | |||
801 | static 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 | |||
855 | static 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 | |||
875 | static 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 | */ | ||
896 | static 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 | |||
960 | static int | ||
961 | armada_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 | |||
987 | static 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 | |||
996 | static 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 | |||
1002 | static 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 | |||
1008 | static 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 | |||
1028 | int 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 | |||
11 | struct armada_gem_object; | ||
12 | |||
13 | struct 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 | |||
34 | struct armada_frame_work; | ||
35 | |||
36 | struct 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 | |||
75 | int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *); | ||
76 | void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); | ||
77 | void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); | ||
78 | void armada_drm_crtc_irq(struct armada_crtc *, u32); | ||
79 | void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); | ||
80 | void armada_drm_crtc_enable_irq(struct armada_crtc *, u32); | ||
81 | void 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 | |||
17 | static 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 | |||
31 | static 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 | |||
53 | static 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 | |||
58 | static 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 | |||
66 | static 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 | |||
98 | static 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 | |||
104 | static 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 | |||
111 | static 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 | |||
116 | static 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 | |||
138 | static 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 | |||
148 | int 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 | |||
177 | void 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 | |||
16 | struct armada_crtc; | ||
17 | struct armada_gem_object; | ||
18 | struct clk; | ||
19 | struct drm_fb_helper; | ||
20 | |||
21 | static inline void | ||
22 | armada_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 | |||
32 | static 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 | |||
40 | struct armada_vbl_event { | ||
41 | struct list_head node; | ||
42 | void *data; | ||
43 | void (*fn)(struct armada_crtc *, void *); | ||
44 | }; | ||
45 | void armada_drm_vbl_event_add(struct armada_crtc *, | ||
46 | struct armada_vbl_event *); | ||
47 | void armada_drm_vbl_event_remove(struct armada_crtc *, | ||
48 | struct armada_vbl_event *); | ||
49 | void 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 | |||
59 | struct armada_private; | ||
60 | |||
61 | struct 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 */ | ||
72 | extern const struct armada_variant armada510_ops; | ||
73 | |||
74 | struct 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 | |||
98 | void __armada_drm_queue_unref_work(struct drm_device *, | ||
99 | struct drm_framebuffer *); | ||
100 | void armada_drm_queue_unref_work(struct drm_device *, | ||
101 | struct drm_framebuffer *); | ||
102 | |||
103 | extern const struct drm_mode_config_funcs armada_drm_mode_config_funcs; | ||
104 | |||
105 | int armada_fbdev_init(struct drm_device *); | ||
106 | void armada_fbdev_fini(struct drm_device *); | ||
107 | |||
108 | int armada_overlay_plane_create(struct drm_device *, unsigned long); | ||
109 | |||
110 | int armada_drm_debugfs_init(struct drm_minor *); | ||
111 | void 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 | |||
23 | static 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 | |||
42 | static 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 = ¶ms, | ||
51 | }, | ||
52 | }; | ||
53 | #endif | ||
54 | |||
55 | static 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 */ | ||
66 | void __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 | |||
83 | void 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 | |||
93 | static 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 | |||
207 | static 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 | |||
222 | void 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 | |||
236 | void 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 | |||
245 | void 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. */ | ||
256 | static 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 | |||
263 | static 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 | |||
269 | static 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 | |||
294 | static 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 | |||
307 | static 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 | |||
315 | static 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 | |||
324 | static 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 | |||
335 | static 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 | |||
372 | static int armada_drm_probe(struct platform_device *pdev) | ||
373 | { | ||
374 | return drm_platform_init(&armada_drm_driver, pdev); | ||
375 | } | ||
376 | |||
377 | static int armada_drm_remove(struct platform_device *pdev) | ||
378 | { | ||
379 | drm_platform_exit(&armada_drm_driver, pdev); | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static 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 | }; | ||
393 | MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids); | ||
394 | |||
395 | static 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 | |||
405 | static 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 | } | ||
410 | module_init(armada_drm_init); | ||
411 | |||
412 | static void __exit armada_drm_exit(void) | ||
413 | { | ||
414 | platform_driver_unregister(&armada_drm_platform_driver); | ||
415 | } | ||
416 | module_exit(armada_drm_exit); | ||
417 | |||
418 | MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>"); | ||
419 | MODULE_DESCRIPTION("Armada DRM Driver"); | ||
420 | MODULE_LICENSE("GPL"); | ||
421 | MODULE_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 | |||
16 | static 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 | |||
25 | static 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 | |||
32 | static const struct drm_framebuffer_funcs armada_fb_funcs = { | ||
33 | .destroy = armada_fb_destroy, | ||
34 | .create_handle = armada_fb_create_handle, | ||
35 | }; | ||
36 | |||
37 | struct 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 | |||
103 | static 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 | |||
158 | static 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 | |||
167 | const 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 | |||
11 | struct 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 | |||
21 | struct 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 | |||
21 | static /*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 | |||
35 | static 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 | |||
121 | static 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 | |||
134 | static 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 | |||
140 | int 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 | |||
180 | void 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 | |||
17 | static 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 | |||
38 | const 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 | |||
44 | static size_t roundup_gem_size(size_t size) | ||
45 | { | ||
46 | return roundup(size, PAGE_SIZE); | ||
47 | } | ||
48 | |||
49 | /* dev->struct_mutex is held here */ | ||
50 | void 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 | |||
82 | int | ||
83 | armada_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 | |||
181 | void * | ||
182 | armada_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 | |||
190 | struct armada_gem_object * | ||
191 | armada_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 | |||
209 | struct 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 */ | ||
237 | int 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 | |||
269 | int 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 | |||
302 | int 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 */ | ||
309 | int 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 */ | ||
341 | int 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 | |||
368 | int 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 */ | ||
419 | struct sg_table * | ||
420 | armada_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 | |||
491 | static 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 | |||
511 | static void *armada_gem_dmabuf_no_kmap(struct dma_buf *buf, unsigned long n) | ||
512 | { | ||
513 | return NULL; | ||
514 | } | ||
515 | |||
516 | static void | ||
517 | armada_gem_dmabuf_no_kunmap(struct dma_buf *buf, unsigned long n, void *addr) | ||
518 | { | ||
519 | } | ||
520 | |||
521 | static int | ||
522 | armada_gem_dmabuf_mmap(struct dma_buf *buf, struct vm_area_struct *vma) | ||
523 | { | ||
524 | return -EINVAL; | ||
525 | } | ||
526 | |||
527 | static 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 | |||
538 | struct dma_buf * | ||
539 | armada_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 | |||
546 | struct drm_gem_object * | ||
547 | armada_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 | |||
585 | int 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 */ | ||
12 | struct 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 | |||
24 | extern 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 | |||
28 | void armada_gem_free_object(struct drm_gem_object *); | ||
29 | int armada_gem_linear_back(struct drm_device *, struct armada_gem_object *); | ||
30 | void *armada_gem_map_object(struct drm_device *, struct armada_gem_object *); | ||
31 | struct armada_gem_object *armada_gem_alloc_private_object(struct drm_device *, | ||
32 | size_t); | ||
33 | int armada_gem_dumb_create(struct drm_file *, struct drm_device *, | ||
34 | struct drm_mode_create_dumb *); | ||
35 | int armada_gem_dumb_map_offset(struct drm_file *, struct drm_device *, | ||
36 | uint32_t, uint64_t *); | ||
37 | int armada_gem_dumb_destroy(struct drm_file *, struct drm_device *, | ||
38 | uint32_t); | ||
39 | struct dma_buf *armada_gem_prime_export(struct drm_device *dev, | ||
40 | struct drm_gem_object *obj, int flags); | ||
41 | struct drm_gem_object *armada_gem_prime_import(struct drm_device *, | ||
42 | struct dma_buf *); | ||
43 | int armada_gem_map_import(struct armada_gem_object *); | ||
44 | |||
45 | static 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 | */ | ||
19 | enum { | ||
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 */ | ||
80 | enum { | ||
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 | |||
91 | enum { | ||
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 */ | ||
109 | enum { | ||
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 | |||
134 | enum { | ||
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 */ | ||
146 | enum { | ||
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 */ | ||
167 | enum { | ||
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 */ | ||
181 | enum { | ||
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 */ | ||
197 | enum { | ||
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 */ | ||
217 | enum { | ||
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 */ | ||
239 | enum { | ||
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 */ | ||
264 | enum { | ||
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 */ | ||
285 | enum { | ||
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)\ | ||
12 | extern int armada_##name##_ioctl(struct drm_device *, void *, struct drm_file *) | ||
13 | |||
14 | ARMADA_IOCTL_PROTO(gem_create); | ||
15 | ARMADA_IOCTL_PROTO(gem_mmap); | ||
16 | ARMADA_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 | |||
15 | struct 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 | |||
22 | struct 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 | |||
29 | static 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 | |||
47 | static 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 | |||
56 | static 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 | |||
67 | static 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 | |||
75 | void armada_drm_encoder_prepare(struct drm_encoder *encoder) | ||
76 | { | ||
77 | encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_OFF); | ||
78 | } | ||
79 | |||
80 | void armada_drm_encoder_commit(struct drm_encoder *encoder) | ||
81 | { | ||
82 | encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_ON); | ||
83 | } | ||
84 | |||
85 | bool 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? */ | ||
92 | int 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 | |||
106 | int 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 | |||
121 | int 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 | |||
14 | struct 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 | |||
22 | struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn); | ||
23 | |||
24 | void armada_drm_encoder_prepare(struct drm_encoder *encoder); | ||
25 | void armada_drm_encoder_commit(struct drm_encoder *encoder); | ||
26 | |||
27 | bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder, | ||
28 | const struct drm_display_mode *mode, struct drm_display_mode *adj); | ||
29 | |||
30 | int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn, | ||
31 | struct drm_display_mode *mode); | ||
32 | |||
33 | int armada_drm_slave_encoder_set_property(struct drm_connector *conn, | ||
34 | struct drm_property *property, uint64_t value); | ||
35 | |||
36 | int 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 | |||
18 | struct 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 | |||
31 | struct 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 | |||
49 | static void | ||
50 | armada_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 === */ | ||
74 | static 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 | |||
90 | static 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 | |||
100 | static int | ||
101 | armada_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 | |||
245 | static 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 | |||
280 | static void armada_plane_destroy(struct drm_plane *plane) | ||
281 | { | ||
282 | kfree(plane); | ||
283 | } | ||
284 | |||
285 | static 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 | |||
353 | static 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 | |||
360 | static 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 | |||
381 | static 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 | |||
392 | static 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 | |||
426 | int 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 | |||
17 | static 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 | |||
31 | static 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 | |||
45 | static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = { | ||
46 | .destroy = armada_drm_slave_destroy, | ||
47 | }; | ||
48 | |||
49 | static 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 | |||
55 | static 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 | |||
66 | static int | ||
67 | armada_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 | |||
129 | static 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 | |||
135 | int 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 | |||
14 | struct 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 | |||
23 | int 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, |