aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2013-06-19 07:54:11 -0400
committerDave Airlie <airlied@redhat.com>2013-06-26 20:08:04 -0400
commit4bf8e1962f91eed5dbee168d2348983dda0a518f (patch)
tree0abd2388e0dee111b0823a73bfca29a234053e83 /drivers
parent0ab3691fa8ac15ed94f9e2fa7c2958ab41d2e3f5 (diff)
drm: Renesas R-Car Display Unit DRM driver
The R-Car Display Unit (DU) DRM driver supports both superposition processors and all eight planes in RGB and YUV formats with alpha blending. Only VGA and LVDS encoders and connectors are currently supported. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig9
-rw-r--r--drivers/gpu/drm/rcar-du/Makefile8
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.c595
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.h50
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.c325
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.h66
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.c245
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.h59
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvds.c216
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvds.h24
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_plane.c507
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_plane.h67
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_regs.h445
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_vga.c149
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_vga.h24
17 files changed, 2792 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index b16c50ee769c..71ca63b79a4f 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -213,6 +213,8 @@ source "drivers/gpu/drm/mgag200/Kconfig"
213 213
214source "drivers/gpu/drm/cirrus/Kconfig" 214source "drivers/gpu/drm/cirrus/Kconfig"
215 215
216source "drivers/gpu/drm/rcar-du/Kconfig"
217
216source "drivers/gpu/drm/shmobile/Kconfig" 218source "drivers/gpu/drm/shmobile/Kconfig"
217 219
218source "drivers/gpu/drm/omapdrm/Kconfig" 220source "drivers/gpu/drm/omapdrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 1ecbe5b7312d..801bcafa3028 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
49obj-$(CONFIG_DRM_GMA500) += gma500/ 49obj-$(CONFIG_DRM_GMA500) += gma500/
50obj-$(CONFIG_DRM_UDL) += udl/ 50obj-$(CONFIG_DRM_UDL) += udl/
51obj-$(CONFIG_DRM_AST) += ast/ 51obj-$(CONFIG_DRM_AST) += ast/
52obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
52obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ 53obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
53obj-$(CONFIG_DRM_OMAP) += omapdrm/ 54obj-$(CONFIG_DRM_OMAP) += omapdrm/
54obj-$(CONFIG_DRM_TILCDC) += tilcdc/ 55obj-$(CONFIG_DRM_TILCDC) += tilcdc/
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
new file mode 100644
index 000000000000..72887df8dd76
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -0,0 +1,9 @@
1config DRM_RCAR_DU
2 tristate "DRM Support for R-Car Display Unit"
3 depends on DRM && ARM
4 select DRM_KMS_HELPER
5 select DRM_KMS_CMA_HELPER
6 select DRM_GEM_CMA_HELPER
7 help
8 Choose this option if you have an R-Car chipset.
9 If M is selected the module will be called rcar-du-drm.
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
new file mode 100644
index 000000000000..7333c0094015
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -0,0 +1,8 @@
1rcar-du-drm-y := rcar_du_crtc.o \
2 rcar_du_drv.o \
3 rcar_du_kms.o \
4 rcar_du_lvds.o \
5 rcar_du_plane.o \
6 rcar_du_vga.o
7
8obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
new file mode 100644
index 000000000000..24183fb93592
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -0,0 +1,595 @@
1/*
2 * rcar_du_crtc.c -- R-Car Display Unit CRTCs
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/clk.h>
15#include <linux/mutex.h>
16
17#include <drm/drmP.h>
18#include <drm/drm_crtc.h>
19#include <drm/drm_crtc_helper.h>
20#include <drm/drm_fb_cma_helper.h>
21#include <drm/drm_gem_cma_helper.h>
22
23#include "rcar_du_crtc.h"
24#include "rcar_du_drv.h"
25#include "rcar_du_kms.h"
26#include "rcar_du_lvds.h"
27#include "rcar_du_plane.h"
28#include "rcar_du_regs.h"
29#include "rcar_du_vga.h"
30
31#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc)
32
33static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
34{
35 struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
36
37 return rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
38}
39
40static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data)
41{
42 struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
43
44 rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data);
45}
46
47static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr)
48{
49 struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
50
51 rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
52 rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr);
53}
54
55static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set)
56{
57 struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
58
59 rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
60 rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set);
61}
62
63static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg,
64 u32 clr, u32 set)
65{
66 struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
67 u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
68
69 rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set);
70}
71
72static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
73{
74 struct drm_crtc *crtc = &rcrtc->crtc;
75 struct rcar_du_device *rcdu = crtc->dev->dev_private;
76 const struct drm_display_mode *mode = &crtc->mode;
77 unsigned long clk;
78 u32 value;
79 u32 div;
80
81 /* Dot clock */
82 clk = clk_get_rate(rcdu->clock);
83 div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000);
84 div = clamp(div, 1U, 64U) - 1;
85
86 rcar_du_write(rcdu, rcrtc->index ? ESCR2 : ESCR,
87 ESCR_DCLKSEL_CLKS | div);
88 rcar_du_write(rcdu, rcrtc->index ? OTAR2 : OTAR, 0);
89
90 /* Signal polarities */
91 value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
92 | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
93 | DSMR_DIPM_DE;
94 rcar_du_crtc_write(rcrtc, DSMR, value);
95
96 /* Display timings */
97 rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19);
98 rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start +
99 mode->hdisplay - 19);
100 rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end -
101 mode->hsync_start - 1);
102 rcar_du_crtc_write(rcrtc, HCR, mode->htotal - 1);
103
104 rcar_du_crtc_write(rcrtc, VDSR, mode->vtotal - mode->vsync_end - 2);
105 rcar_du_crtc_write(rcrtc, VDER, mode->vtotal - mode->vsync_end +
106 mode->vdisplay - 2);
107 rcar_du_crtc_write(rcrtc, VSPR, mode->vtotal - mode->vsync_end +
108 mode->vsync_start - 1);
109 rcar_du_crtc_write(rcrtc, VCR, mode->vtotal - 1);
110
111 rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start);
112 rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay);
113}
114
115static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc)
116{
117 struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
118 u32 dorcr = rcar_du_read(rcdu, DORCR);
119
120 dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
121
122 /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and
123 * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by
124 * default.
125 */
126 if (rcrtc->outputs & (1 << 1) && rcrtc->index == 0)
127 dorcr |= DORCR_PG2D_DS1;
128 else
129 dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
130
131 rcar_du_write(rcdu, DORCR, dorcr);
132}
133
134static void __rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
135{
136 rcar_du_write(rcdu, DSYSR,
137 (rcar_du_read(rcdu, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) |
138 (start ? DSYSR_DEN : DSYSR_DRES));
139}
140
141static void rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
142{
143 /* Many of the configuration bits are only updated when the display
144 * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some
145 * of those bits could be pre-configured, but others (especially the
146 * bits related to plane assignment to display timing controllers) need
147 * to be modified at runtime.
148 *
149 * Restart the display controller if a start is requested. Sorry for the
150 * flicker. It should be possible to move most of the "DRES-update" bits
151 * setup to driver initialization time and minimize the number of cases
152 * when the display controller will have to be restarted.
153 */
154 if (start) {
155 if (rcdu->used_crtcs++ != 0)
156 __rcar_du_start_stop(rcdu, false);
157 __rcar_du_start_stop(rcdu, true);
158 } else {
159 if (--rcdu->used_crtcs == 0)
160 __rcar_du_start_stop(rcdu, false);
161 }
162}
163
164void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output)
165{
166 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
167
168 /* Store the route from the CRTC output to the DU output. The DU will be
169 * configured when starting the CRTC.
170 */
171 rcrtc->outputs |= 1 << output;
172}
173
174void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
175{
176 struct rcar_du_device *rcdu = crtc->dev->dev_private;
177 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
178 struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
179 unsigned int num_planes = 0;
180 unsigned int prio = 0;
181 unsigned int i;
182 u32 dptsr = 0;
183 u32 dspr = 0;
184
185 for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
186 struct rcar_du_plane *plane = &rcdu->planes.planes[i];
187 unsigned int j;
188
189 if (plane->crtc != &rcrtc->crtc || !plane->enabled)
190 continue;
191
192 /* Insert the plane in the sorted planes array. */
193 for (j = num_planes++; j > 0; --j) {
194 if (planes[j-1]->zpos <= plane->zpos)
195 break;
196 planes[j] = planes[j-1];
197 }
198
199 planes[j] = plane;
200 prio += plane->format->planes * 4;
201 }
202
203 for (i = 0; i < num_planes; ++i) {
204 struct rcar_du_plane *plane = planes[i];
205 unsigned int index = plane->hwindex;
206
207 prio -= 4;
208 dspr |= (index + 1) << prio;
209 dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index);
210
211 if (plane->format->planes == 2) {
212 index = (index + 1) % 8;
213
214 prio -= 4;
215 dspr |= (index + 1) << prio;
216 dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index);
217 }
218 }
219
220 /* Select display timing and dot clock generator 2 for planes associated
221 * with superposition controller 2.
222 */
223 if (rcrtc->index) {
224 u32 value = rcar_du_read(rcdu, DPTSR);
225
226 /* The DPTSR register is updated when the display controller is
227 * stopped. We thus need to restart the DU. Once again, sorry
228 * for the flicker. One way to mitigate the issue would be to
229 * pre-associate planes with CRTCs (either with a fixed 4/4
230 * split, or through a module parameter). Flicker would then
231 * occur only if we need to break the pre-association.
232 */
233 if (value != dptsr) {
234 rcar_du_write(rcdu, DPTSR, dptsr);
235 if (rcdu->used_crtcs) {
236 __rcar_du_start_stop(rcdu, false);
237 __rcar_du_start_stop(rcdu, true);
238 }
239 }
240 }
241
242 rcar_du_write(rcdu, rcrtc->index ? DS2PR : DS1PR, dspr);
243}
244
245static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
246{
247 struct drm_crtc *crtc = &rcrtc->crtc;
248 struct rcar_du_device *rcdu = crtc->dev->dev_private;
249 unsigned int i;
250
251 if (rcrtc->started)
252 return;
253
254 if (WARN_ON(rcrtc->plane->format == NULL))
255 return;
256
257 /* Set display off and background to black */
258 rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
259 rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
260
261 /* Configure display timings and output routing */
262 rcar_du_crtc_set_display_timing(rcrtc);
263 rcar_du_crtc_set_routing(rcrtc);
264
265 mutex_lock(&rcdu->planes.lock);
266 rcrtc->plane->enabled = true;
267 rcar_du_crtc_update_planes(crtc);
268 mutex_unlock(&rcdu->planes.lock);
269
270 /* Setup planes. */
271 for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
272 struct rcar_du_plane *plane = &rcdu->planes.planes[i];
273
274 if (plane->crtc != crtc || !plane->enabled)
275 continue;
276
277 rcar_du_plane_setup(plane);
278 }
279
280 /* Select master sync mode. This enables display operation in master
281 * sync mode (with the HSYNC and VSYNC signals configured as outputs and
282 * actively driven).
283 */
284 rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER);
285
286 rcar_du_start_stop(rcdu, true);
287
288 rcrtc->started = true;
289}
290
291static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
292{
293 struct drm_crtc *crtc = &rcrtc->crtc;
294 struct rcar_du_device *rcdu = crtc->dev->dev_private;
295
296 if (!rcrtc->started)
297 return;
298
299 mutex_lock(&rcdu->planes.lock);
300 rcrtc->plane->enabled = false;
301 rcar_du_crtc_update_planes(crtc);
302 mutex_unlock(&rcdu->planes.lock);
303
304 /* Select switch sync mode. This stops display operation and configures
305 * the HSYNC and VSYNC signals as inputs.
306 */
307 rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH);
308
309 rcar_du_start_stop(rcdu, false);
310
311 rcrtc->started = false;
312}
313
314void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc)
315{
316 struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
317
318 rcar_du_crtc_stop(rcrtc);
319 rcar_du_put(rcdu);
320}
321
322void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc)
323{
324 struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
325
326 if (rcrtc->dpms != DRM_MODE_DPMS_ON)
327 return;
328
329 rcar_du_get(rcdu);
330 rcar_du_crtc_start(rcrtc);
331}
332
333static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc)
334{
335 struct drm_crtc *crtc = &rcrtc->crtc;
336
337 rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
338 rcar_du_plane_update_base(rcrtc->plane);
339}
340
341static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode)
342{
343 struct rcar_du_device *rcdu = crtc->dev->dev_private;
344 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
345
346 if (rcrtc->dpms == mode)
347 return;
348
349 if (mode == DRM_MODE_DPMS_ON) {
350 rcar_du_get(rcdu);
351 rcar_du_crtc_start(rcrtc);
352 } else {
353 rcar_du_crtc_stop(rcrtc);
354 rcar_du_put(rcdu);
355 }
356
357 rcrtc->dpms = mode;
358}
359
360static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc,
361 const struct drm_display_mode *mode,
362 struct drm_display_mode *adjusted_mode)
363{
364 /* TODO Fixup modes */
365 return true;
366}
367
368static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc)
369{
370 struct rcar_du_device *rcdu = crtc->dev->dev_private;
371 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
372
373 /* We need to access the hardware during mode set, acquire a reference
374 * to the DU.
375 */
376 rcar_du_get(rcdu);
377
378 /* Stop the CRTC and release the plane. Force the DPMS mode to off as a
379 * result.
380 */
381 rcar_du_crtc_stop(rcrtc);
382 rcar_du_plane_release(rcrtc->plane);
383
384 rcrtc->dpms = DRM_MODE_DPMS_OFF;
385}
386
387static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
388 struct drm_display_mode *mode,
389 struct drm_display_mode *adjusted_mode,
390 int x, int y,
391 struct drm_framebuffer *old_fb)
392{
393 struct rcar_du_device *rcdu = crtc->dev->dev_private;
394 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
395 const struct rcar_du_format_info *format;
396 int ret;
397
398 format = rcar_du_format_info(crtc->fb->pixel_format);
399 if (format == NULL) {
400 dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n",
401 crtc->fb->pixel_format);
402 ret = -EINVAL;
403 goto error;
404 }
405
406 ret = rcar_du_plane_reserve(rcrtc->plane, format);
407 if (ret < 0)
408 goto error;
409
410 rcrtc->plane->format = format;
411 rcrtc->plane->pitch = crtc->fb->pitches[0];
412
413 rcrtc->plane->src_x = x;
414 rcrtc->plane->src_y = y;
415 rcrtc->plane->width = mode->hdisplay;
416 rcrtc->plane->height = mode->vdisplay;
417
418 rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
419
420 rcrtc->outputs = 0;
421
422 return 0;
423
424error:
425 /* There's no rollback/abort operation to clean up in case of error. We
426 * thus need to release the reference to the DU acquired in prepare()
427 * here.
428 */
429 rcar_du_put(rcdu);
430 return ret;
431}
432
433static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc)
434{
435 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
436
437 /* We're done, restart the CRTC and set the DPMS mode to on. The
438 * reference to the DU acquired at prepare() time will thus be released
439 * by the DPMS handler (possibly called by the disable() handler).
440 */
441 rcar_du_crtc_start(rcrtc);
442 rcrtc->dpms = DRM_MODE_DPMS_ON;
443}
444
445static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
446 struct drm_framebuffer *old_fb)
447{
448 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
449
450 rcrtc->plane->src_x = x;
451 rcrtc->plane->src_y = y;
452
453 rcar_du_crtc_update_base(to_rcar_crtc(crtc));
454
455 return 0;
456}
457
458static void rcar_du_crtc_disable(struct drm_crtc *crtc)
459{
460 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
461
462 rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
463 rcar_du_plane_release(rcrtc->plane);
464}
465
466static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
467 .dpms = rcar_du_crtc_dpms,
468 .mode_fixup = rcar_du_crtc_mode_fixup,
469 .prepare = rcar_du_crtc_mode_prepare,
470 .commit = rcar_du_crtc_mode_commit,
471 .mode_set = rcar_du_crtc_mode_set,
472 .mode_set_base = rcar_du_crtc_mode_set_base,
473 .disable = rcar_du_crtc_disable,
474};
475
476void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
477 struct drm_file *file)
478{
479 struct drm_pending_vblank_event *event;
480 struct drm_device *dev = rcrtc->crtc.dev;
481 unsigned long flags;
482
483 /* Destroy the pending vertical blanking event associated with the
484 * pending page flip, if any, and disable vertical blanking interrupts.
485 */
486 spin_lock_irqsave(&dev->event_lock, flags);
487 event = rcrtc->event;
488 if (event && event->base.file_priv == file) {
489 rcrtc->event = NULL;
490 event->base.destroy(&event->base);
491 drm_vblank_put(dev, rcrtc->index);
492 }
493 spin_unlock_irqrestore(&dev->event_lock, flags);
494}
495
496static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
497{
498 struct drm_pending_vblank_event *event;
499 struct drm_device *dev = rcrtc->crtc.dev;
500 unsigned long flags;
501
502 spin_lock_irqsave(&dev->event_lock, flags);
503 event = rcrtc->event;
504 rcrtc->event = NULL;
505 spin_unlock_irqrestore(&dev->event_lock, flags);
506
507 if (event == NULL)
508 return;
509
510 spin_lock_irqsave(&dev->event_lock, flags);
511 drm_send_vblank_event(dev, rcrtc->index, event);
512 spin_unlock_irqrestore(&dev->event_lock, flags);
513
514 drm_vblank_put(dev, rcrtc->index);
515}
516
517static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
518 struct drm_framebuffer *fb,
519 struct drm_pending_vblank_event *event)
520{
521 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
522 struct drm_device *dev = rcrtc->crtc.dev;
523 unsigned long flags;
524
525 spin_lock_irqsave(&dev->event_lock, flags);
526 if (rcrtc->event != NULL) {
527 spin_unlock_irqrestore(&dev->event_lock, flags);
528 return -EBUSY;
529 }
530 spin_unlock_irqrestore(&dev->event_lock, flags);
531
532 crtc->fb = fb;
533 rcar_du_crtc_update_base(rcrtc);
534
535 if (event) {
536 event->pipe = rcrtc->index;
537 drm_vblank_get(dev, rcrtc->index);
538 spin_lock_irqsave(&dev->event_lock, flags);
539 rcrtc->event = event;
540 spin_unlock_irqrestore(&dev->event_lock, flags);
541 }
542
543 return 0;
544}
545
546static const struct drm_crtc_funcs crtc_funcs = {
547 .destroy = drm_crtc_cleanup,
548 .set_config = drm_crtc_helper_set_config,
549 .page_flip = rcar_du_crtc_page_flip,
550};
551
552int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index)
553{
554 struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index];
555 struct drm_crtc *crtc = &rcrtc->crtc;
556 int ret;
557
558 rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0;
559 rcrtc->index = index;
560 rcrtc->dpms = DRM_MODE_DPMS_OFF;
561 rcrtc->plane = &rcdu->planes.planes[index];
562
563 rcrtc->plane->crtc = crtc;
564
565 ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs);
566 if (ret < 0)
567 return ret;
568
569 drm_crtc_helper_add(crtc, &crtc_helper_funcs);
570
571 return 0;
572}
573
574void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable)
575{
576 if (enable) {
577 rcar_du_crtc_write(rcrtc, DSRCR, DSRCR_VBCL);
578 rcar_du_crtc_set(rcrtc, DIER, DIER_VBE);
579 } else {
580 rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE);
581 }
582}
583
584void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc)
585{
586 u32 status;
587
588 status = rcar_du_crtc_read(rcrtc, DSSR);
589 rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK);
590
591 if (status & DSSR_VBK) {
592 drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index);
593 rcar_du_crtc_finish_page_flip(rcrtc);
594 }
595}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
new file mode 100644
index 000000000000..2a0365bcbd14
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -0,0 +1,50 @@
1/*
2 * rcar_du_crtc.h -- R-Car Display Unit CRTCs
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __RCAR_DU_CRTC_H__
15#define __RCAR_DU_CRTC_H__
16
17#include <linux/mutex.h>
18
19#include <drm/drmP.h>
20#include <drm/drm_crtc.h>
21
22struct rcar_du_device;
23struct rcar_du_plane;
24
25struct rcar_du_crtc {
26 struct drm_crtc crtc;
27
28 unsigned int mmio_offset;
29 unsigned int index;
30 bool started;
31
32 struct drm_pending_vblank_event *event;
33 unsigned int outputs;
34 int dpms;
35
36 struct rcar_du_plane *plane;
37};
38
39int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index);
40void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable);
41void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc);
42void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
43 struct drm_file *file);
44void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc);
45void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
46
47void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output);
48void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
49
50#endif /* __RCAR_DU_CRTC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
new file mode 100644
index 000000000000..003b34ee38e3
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -0,0 +1,325 @@
1/*
2 * rcar_du_drv.c -- R-Car Display Unit DRM driver
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/clk.h>
15#include <linux/io.h>
16#include <linux/mm.h>
17#include <linux/module.h>
18#include <linux/platform_device.h>
19#include <linux/pm.h>
20#include <linux/slab.h>
21
22#include <drm/drmP.h>
23#include <drm/drm_crtc_helper.h>
24#include <drm/drm_gem_cma_helper.h>
25
26#include "rcar_du_crtc.h"
27#include "rcar_du_drv.h"
28#include "rcar_du_kms.h"
29#include "rcar_du_regs.h"
30
31/* -----------------------------------------------------------------------------
32 * Core device operations
33 */
34
35/*
36 * rcar_du_get - Acquire a reference to the DU
37 *
38 * Acquiring a reference enables the device clock and setup core registers. A
39 * reference must be held before accessing any hardware registers.
40 *
41 * This function must be called with the DRM mode_config lock held.
42 *
43 * Return 0 in case of success or a negative error code otherwise.
44 */
45int rcar_du_get(struct rcar_du_device *rcdu)
46{
47 int ret;
48
49 if (rcdu->use_count)
50 goto done;
51
52 /* Enable clocks before accessing the hardware. */
53 ret = clk_prepare_enable(rcdu->clock);
54 if (ret < 0)
55 return ret;
56
57 /* Enable extended features */
58 rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE);
59 rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G);
60 rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
61 rcar_du_write(rcdu, DEFR4, DEFR4_CODE);
62 rcar_du_write(rcdu, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
63
64 /* Use DS1PR and DS2PR to configure planes priorities and connects the
65 * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
66 */
67 rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS);
68
69done:
70 rcdu->use_count++;
71 return 0;
72}
73
74/*
75 * rcar_du_put - Release a reference to the DU
76 *
77 * Releasing the last reference disables the device clock.
78 *
79 * This function must be called with the DRM mode_config lock held.
80 */
81void rcar_du_put(struct rcar_du_device *rcdu)
82{
83 if (--rcdu->use_count)
84 return;
85
86 clk_disable_unprepare(rcdu->clock);
87}
88
89/* -----------------------------------------------------------------------------
90 * DRM operations
91 */
92
93static int rcar_du_unload(struct drm_device *dev)
94{
95 drm_kms_helper_poll_fini(dev);
96 drm_mode_config_cleanup(dev);
97 drm_vblank_cleanup(dev);
98 drm_irq_uninstall(dev);
99
100 dev->dev_private = NULL;
101
102 return 0;
103}
104
105static int rcar_du_load(struct drm_device *dev, unsigned long flags)
106{
107 struct platform_device *pdev = dev->platformdev;
108 struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
109 struct rcar_du_device *rcdu;
110 struct resource *ioarea;
111 struct resource *mem;
112 int ret;
113
114 if (pdata == NULL) {
115 dev_err(dev->dev, "no platform data\n");
116 return -ENODEV;
117 }
118
119 rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
120 if (rcdu == NULL) {
121 dev_err(dev->dev, "failed to allocate private data\n");
122 return -ENOMEM;
123 }
124
125 rcdu->dev = &pdev->dev;
126 rcdu->pdata = pdata;
127 rcdu->ddev = dev;
128 dev->dev_private = rcdu;
129
130 /* I/O resources and clocks */
131 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
132 if (mem == NULL) {
133 dev_err(&pdev->dev, "failed to get memory resource\n");
134 return -EINVAL;
135 }
136
137 ioarea = devm_request_mem_region(&pdev->dev, mem->start,
138 resource_size(mem), pdev->name);
139 if (ioarea == NULL) {
140 dev_err(&pdev->dev, "failed to request memory region\n");
141 return -EBUSY;
142 }
143
144 rcdu->mmio = devm_ioremap_nocache(&pdev->dev, ioarea->start,
145 resource_size(ioarea));
146 if (rcdu->mmio == NULL) {
147 dev_err(&pdev->dev, "failed to remap memory resource\n");
148 return -ENOMEM;
149 }
150
151 rcdu->clock = devm_clk_get(&pdev->dev, NULL);
152 if (IS_ERR(rcdu->clock)) {
153 dev_err(&pdev->dev, "failed to get clock\n");
154 return -ENOENT;
155 }
156
157 /* DRM/KMS objects */
158 ret = rcar_du_modeset_init(rcdu);
159 if (ret < 0) {
160 dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
161 goto done;
162 }
163
164 /* IRQ and vblank handling */
165 ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1);
166 if (ret < 0) {
167 dev_err(&pdev->dev, "failed to initialize vblank\n");
168 goto done;
169 }
170
171 ret = drm_irq_install(dev);
172 if (ret < 0) {
173 dev_err(&pdev->dev, "failed to install IRQ handler\n");
174 goto done;
175 }
176
177 platform_set_drvdata(pdev, rcdu);
178
179done:
180 if (ret)
181 rcar_du_unload(dev);
182
183 return ret;
184}
185
186static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file)
187{
188 struct rcar_du_device *rcdu = dev->dev_private;
189 unsigned int i;
190
191 for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
192 rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file);
193}
194
195static irqreturn_t rcar_du_irq(int irq, void *arg)
196{
197 struct drm_device *dev = arg;
198 struct rcar_du_device *rcdu = dev->dev_private;
199 unsigned int i;
200
201 for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
202 rcar_du_crtc_irq(&rcdu->crtcs[i]);
203
204 return IRQ_HANDLED;
205}
206
207static int rcar_du_enable_vblank(struct drm_device *dev, int crtc)
208{
209 struct rcar_du_device *rcdu = dev->dev_private;
210
211 rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], true);
212
213 return 0;
214}
215
216static void rcar_du_disable_vblank(struct drm_device *dev, int crtc)
217{
218 struct rcar_du_device *rcdu = dev->dev_private;
219
220 rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], false);
221}
222
223static const struct file_operations rcar_du_fops = {
224 .owner = THIS_MODULE,
225 .open = drm_open,
226 .release = drm_release,
227 .unlocked_ioctl = drm_ioctl,
228#ifdef CONFIG_COMPAT
229 .compat_ioctl = drm_compat_ioctl,
230#endif
231 .poll = drm_poll,
232 .read = drm_read,
233 .fasync = drm_fasync,
234 .llseek = no_llseek,
235 .mmap = drm_gem_cma_mmap,
236};
237
238static struct drm_driver rcar_du_driver = {
239 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
240 | DRIVER_PRIME,
241 .load = rcar_du_load,
242 .unload = rcar_du_unload,
243 .preclose = rcar_du_preclose,
244 .irq_handler = rcar_du_irq,
245 .get_vblank_counter = drm_vblank_count,
246 .enable_vblank = rcar_du_enable_vblank,
247 .disable_vblank = rcar_du_disable_vblank,
248 .gem_free_object = drm_gem_cma_free_object,
249 .gem_vm_ops = &drm_gem_cma_vm_ops,
250 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
251 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
252 .gem_prime_import = drm_gem_cma_dmabuf_import,
253 .gem_prime_export = drm_gem_cma_dmabuf_export,
254 .dumb_create = drm_gem_cma_dumb_create,
255 .dumb_map_offset = drm_gem_cma_dumb_map_offset,
256 .dumb_destroy = drm_gem_cma_dumb_destroy,
257 .fops = &rcar_du_fops,
258 .name = "rcar-du",
259 .desc = "Renesas R-Car Display Unit",
260 .date = "20130110",
261 .major = 1,
262 .minor = 0,
263};
264
265/* -----------------------------------------------------------------------------
266 * Power management
267 */
268
269#if CONFIG_PM_SLEEP
270static int rcar_du_pm_suspend(struct device *dev)
271{
272 struct rcar_du_device *rcdu = dev_get_drvdata(dev);
273
274 drm_kms_helper_poll_disable(rcdu->ddev);
275 /* TODO Suspend the CRTC */
276
277 return 0;
278}
279
280static int rcar_du_pm_resume(struct device *dev)
281{
282 struct rcar_du_device *rcdu = dev_get_drvdata(dev);
283
284 /* TODO Resume the CRTC */
285
286 drm_kms_helper_poll_enable(rcdu->ddev);
287 return 0;
288}
289#endif
290
291static const struct dev_pm_ops rcar_du_pm_ops = {
292 SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
293};
294
295/* -----------------------------------------------------------------------------
296 * Platform driver
297 */
298
299static int rcar_du_probe(struct platform_device *pdev)
300{
301 return drm_platform_init(&rcar_du_driver, pdev);
302}
303
304static int rcar_du_remove(struct platform_device *pdev)
305{
306 drm_platform_exit(&rcar_du_driver, pdev);
307
308 return 0;
309}
310
311static struct platform_driver rcar_du_platform_driver = {
312 .probe = rcar_du_probe,
313 .remove = rcar_du_remove,
314 .driver = {
315 .owner = THIS_MODULE,
316 .name = "rcar-du",
317 .pm = &rcar_du_pm_ops,
318 },
319};
320
321module_platform_driver(rcar_du_platform_driver);
322
323MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
324MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
325MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
new file mode 100644
index 000000000000..193cc59d495c
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -0,0 +1,66 @@
1/*
2 * rcar_du_drv.h -- R-Car Display Unit DRM driver
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __RCAR_DU_DRV_H__
15#define __RCAR_DU_DRV_H__
16
17#include <linux/kernel.h>
18#include <linux/mutex.h>
19#include <linux/platform_data/rcar-du.h>
20
21#include "rcar_du_crtc.h"
22#include "rcar_du_plane.h"
23
24struct clk;
25struct device;
26struct drm_device;
27
28struct rcar_du_device {
29 struct device *dev;
30 const struct rcar_du_platform_data *pdata;
31
32 void __iomem *mmio;
33 struct clk *clock;
34 unsigned int use_count;
35
36 struct drm_device *ddev;
37
38 struct rcar_du_crtc crtcs[2];
39 unsigned int used_crtcs;
40 unsigned int num_crtcs;
41
42 struct {
43 struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES];
44 unsigned int free;
45 struct mutex lock;
46
47 struct drm_property *alpha;
48 struct drm_property *colorkey;
49 struct drm_property *zpos;
50 } planes;
51};
52
53int rcar_du_get(struct rcar_du_device *rcdu);
54void rcar_du_put(struct rcar_du_device *rcdu);
55
56static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg)
57{
58 return ioread32(rcdu->mmio + reg);
59}
60
61static inline void rcar_du_write(struct rcar_du_device *rcdu, u32 reg, u32 data)
62{
63 iowrite32(data, rcdu->mmio + reg);
64}
65
66#endif /* __RCAR_DU_DRV_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
new file mode 100644
index 000000000000..9c63f39658de
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -0,0 +1,245 @@
1/*
2 * rcar_du_kms.c -- R-Car Display Unit Mode Setting
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <drm/drmP.h>
15#include <drm/drm_crtc.h>
16#include <drm/drm_crtc_helper.h>
17#include <drm/drm_fb_cma_helper.h>
18#include <drm/drm_gem_cma_helper.h>
19
20#include "rcar_du_crtc.h"
21#include "rcar_du_drv.h"
22#include "rcar_du_kms.h"
23#include "rcar_du_lvds.h"
24#include "rcar_du_regs.h"
25#include "rcar_du_vga.h"
26
27/* -----------------------------------------------------------------------------
28 * Format helpers
29 */
30
31static const struct rcar_du_format_info rcar_du_format_infos[] = {
32 {
33 .fourcc = DRM_FORMAT_RGB565,
34 .bpp = 16,
35 .planes = 1,
36 .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
37 .edf = PnDDCR4_EDF_NONE,
38 }, {
39 .fourcc = DRM_FORMAT_ARGB1555,
40 .bpp = 16,
41 .planes = 1,
42 .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
43 .edf = PnDDCR4_EDF_NONE,
44 }, {
45 .fourcc = DRM_FORMAT_XRGB1555,
46 .bpp = 16,
47 .planes = 1,
48 .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
49 .edf = PnDDCR4_EDF_NONE,
50 }, {
51 .fourcc = DRM_FORMAT_XRGB8888,
52 .bpp = 32,
53 .planes = 1,
54 .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
55 .edf = PnDDCR4_EDF_RGB888,
56 }, {
57 .fourcc = DRM_FORMAT_ARGB8888,
58 .bpp = 32,
59 .planes = 1,
60 .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
61 .edf = PnDDCR4_EDF_ARGB8888,
62 }, {
63 .fourcc = DRM_FORMAT_UYVY,
64 .bpp = 16,
65 .planes = 1,
66 .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
67 .edf = PnDDCR4_EDF_NONE,
68 }, {
69 .fourcc = DRM_FORMAT_YUYV,
70 .bpp = 16,
71 .planes = 1,
72 .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
73 .edf = PnDDCR4_EDF_NONE,
74 }, {
75 .fourcc = DRM_FORMAT_NV12,
76 .bpp = 12,
77 .planes = 2,
78 .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
79 .edf = PnDDCR4_EDF_NONE,
80 }, {
81 .fourcc = DRM_FORMAT_NV21,
82 .bpp = 12,
83 .planes = 2,
84 .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
85 .edf = PnDDCR4_EDF_NONE,
86 }, {
87 /* In YUV 4:2:2, only NV16 is supported (NV61 isn't) */
88 .fourcc = DRM_FORMAT_NV16,
89 .bpp = 16,
90 .planes = 2,
91 .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
92 .edf = PnDDCR4_EDF_NONE,
93 },
94};
95
96const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc)
97{
98 unsigned int i;
99
100 for (i = 0; i < ARRAY_SIZE(rcar_du_format_infos); ++i) {
101 if (rcar_du_format_infos[i].fourcc == fourcc)
102 return &rcar_du_format_infos[i];
103 }
104
105 return NULL;
106}
107
108/* -----------------------------------------------------------------------------
109 * Common connector and encoder functions
110 */
111
112struct drm_encoder *
113rcar_du_connector_best_encoder(struct drm_connector *connector)
114{
115 struct rcar_du_connector *rcon = to_rcar_connector(connector);
116
117 return &rcon->encoder->encoder;
118}
119
120void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder)
121{
122}
123
124void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
125 struct drm_display_mode *mode,
126 struct drm_display_mode *adjusted_mode)
127{
128 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
129
130 rcar_du_crtc_route_output(encoder->crtc, renc->output);
131}
132
133void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
134{
135}
136
137/* -----------------------------------------------------------------------------
138 * Frame buffer
139 */
140
141static struct drm_framebuffer *
142rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
143 struct drm_mode_fb_cmd2 *mode_cmd)
144{
145 const struct rcar_du_format_info *format;
146
147 format = rcar_du_format_info(mode_cmd->pixel_format);
148 if (format == NULL) {
149 dev_dbg(dev->dev, "unsupported pixel format %08x\n",
150 mode_cmd->pixel_format);
151 return ERR_PTR(-EINVAL);
152 }
153
154 if (mode_cmd->pitches[0] & 15 || mode_cmd->pitches[0] >= 8192) {
155 dev_dbg(dev->dev, "invalid pitch value %u\n",
156 mode_cmd->pitches[0]);
157 return ERR_PTR(-EINVAL);
158 }
159
160 if (format->planes == 2) {
161 if (mode_cmd->pitches[1] != mode_cmd->pitches[0]) {
162 dev_dbg(dev->dev,
163 "luma and chroma pitches do not match\n");
164 return ERR_PTR(-EINVAL);
165 }
166 }
167
168 return drm_fb_cma_create(dev, file_priv, mode_cmd);
169}
170
171static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
172 .fb_create = rcar_du_fb_create,
173};
174
175int rcar_du_modeset_init(struct rcar_du_device *rcdu)
176{
177 struct drm_device *dev = rcdu->ddev;
178 struct drm_encoder *encoder;
179 unsigned int i;
180 int ret;
181
182 drm_mode_config_init(rcdu->ddev);
183
184 rcdu->ddev->mode_config.min_width = 0;
185 rcdu->ddev->mode_config.min_height = 0;
186 rcdu->ddev->mode_config.max_width = 4095;
187 rcdu->ddev->mode_config.max_height = 2047;
188 rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs;
189
190 ret = rcar_du_plane_init(rcdu);
191 if (ret < 0)
192 return ret;
193
194 for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
195 rcar_du_crtc_create(rcdu, i);
196
197 rcdu->used_crtcs = 0;
198 rcdu->num_crtcs = i;
199
200 for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
201 const struct rcar_du_encoder_data *pdata =
202 &rcdu->pdata->encoders[i];
203
204 if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) {
205 dev_warn(rcdu->dev,
206 "encoder %u references unexisting output %u, skipping\n",
207 i, pdata->output);
208 continue;
209 }
210
211 switch (pdata->encoder) {
212 case RCAR_DU_ENCODER_VGA:
213 rcar_du_vga_init(rcdu, &pdata->u.vga, pdata->output);
214 break;
215
216 case RCAR_DU_ENCODER_LVDS:
217 rcar_du_lvds_init(rcdu, &pdata->u.lvds, pdata->output);
218 break;
219
220 default:
221 break;
222 }
223 }
224
225 /* Set the possible CRTCs and possible clones. All encoders can be
226 * driven by the CRTC associated with the output they're connected to,
227 * as well as by CRTC 0.
228 */
229 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
230 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
231
232 encoder->possible_crtcs = (1 << 0) | (1 << renc->output);
233 encoder->possible_clones = 1 << 0;
234 }
235
236 ret = rcar_du_plane_register(rcdu);
237 if (ret < 0)
238 return ret;
239
240 drm_kms_helper_poll_init(rcdu->ddev);
241
242 drm_helper_disable_unused_functions(rcdu->ddev);
243
244 return 0;
245}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
new file mode 100644
index 000000000000..e4d8db069a06
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
@@ -0,0 +1,59 @@
1/*
2 * rcar_du_kms.h -- R-Car Display Unit Mode Setting
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __RCAR_DU_KMS_H__
15#define __RCAR_DU_KMS_H__
16
17#include <linux/types.h>
18
19#include <drm/drm_crtc.h>
20
21struct rcar_du_device;
22
23struct rcar_du_format_info {
24 u32 fourcc;
25 unsigned int bpp;
26 unsigned int planes;
27 unsigned int pnmr;
28 unsigned int edf;
29};
30
31struct rcar_du_encoder {
32 struct drm_encoder encoder;
33 unsigned int output;
34};
35
36#define to_rcar_encoder(e) \
37 container_of(e, struct rcar_du_encoder, encoder)
38
39struct rcar_du_connector {
40 struct drm_connector connector;
41 struct rcar_du_encoder *encoder;
42};
43
44#define to_rcar_connector(c) \
45 container_of(c, struct rcar_du_connector, connector)
46
47const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc);
48
49struct drm_encoder *
50rcar_du_connector_best_encoder(struct drm_connector *connector);
51void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder);
52void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
53 struct drm_display_mode *mode,
54 struct drm_display_mode *adjusted_mode);
55void rcar_du_encoder_mode_commit(struct drm_encoder *encoder);
56
57int rcar_du_modeset_init(struct rcar_du_device *rcdu);
58
59#endif /* __RCAR_DU_KMS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c
new file mode 100644
index 000000000000..7aefe7267e1d
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvds.c
@@ -0,0 +1,216 @@
1/*
2 * rcar_du_lvds.c -- R-Car Display Unit LVDS Encoder and Connector
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <drm/drmP.h>
15#include <drm/drm_crtc.h>
16#include <drm/drm_crtc_helper.h>
17
18#include "rcar_du_drv.h"
19#include "rcar_du_kms.h"
20#include "rcar_du_lvds.h"
21
22struct rcar_du_lvds_connector {
23 struct rcar_du_connector connector;
24
25 const struct rcar_du_panel_data *panel;
26};
27
28#define to_rcar_lvds_connector(c) \
29 container_of(c, struct rcar_du_lvds_connector, connector.connector)
30
31/* -----------------------------------------------------------------------------
32 * Connector
33 */
34
35static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
36{
37 struct rcar_du_lvds_connector *lvdscon = to_rcar_lvds_connector(connector);
38 struct drm_display_mode *mode;
39
40 mode = drm_mode_create(connector->dev);
41 if (mode == NULL)
42 return 0;
43
44 mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
45 mode->clock = lvdscon->panel->mode.clock;
46 mode->hdisplay = lvdscon->panel->mode.hdisplay;
47 mode->hsync_start = lvdscon->panel->mode.hsync_start;
48 mode->hsync_end = lvdscon->panel->mode.hsync_end;
49 mode->htotal = lvdscon->panel->mode.htotal;
50 mode->vdisplay = lvdscon->panel->mode.vdisplay;
51 mode->vsync_start = lvdscon->panel->mode.vsync_start;
52 mode->vsync_end = lvdscon->panel->mode.vsync_end;
53 mode->vtotal = lvdscon->panel->mode.vtotal;
54 mode->flags = lvdscon->panel->mode.flags;
55
56 drm_mode_set_name(mode);
57 drm_mode_probed_add(connector, mode);
58
59 return 1;
60}
61
62static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector,
63 struct drm_display_mode *mode)
64{
65 return MODE_OK;
66}
67
68static const struct drm_connector_helper_funcs connector_helper_funcs = {
69 .get_modes = rcar_du_lvds_connector_get_modes,
70 .mode_valid = rcar_du_lvds_connector_mode_valid,
71 .best_encoder = rcar_du_connector_best_encoder,
72};
73
74static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
75{
76 drm_sysfs_connector_remove(connector);
77 drm_connector_cleanup(connector);
78}
79
80static enum drm_connector_status
81rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
82{
83 return connector_status_connected;
84}
85
86static const struct drm_connector_funcs connector_funcs = {
87 .dpms = drm_helper_connector_dpms,
88 .detect = rcar_du_lvds_connector_detect,
89 .fill_modes = drm_helper_probe_single_connector_modes,
90 .destroy = rcar_du_lvds_connector_destroy,
91};
92
93static int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
94 struct rcar_du_encoder *renc,
95 const struct rcar_du_panel_data *panel)
96{
97 struct rcar_du_lvds_connector *lvdscon;
98 struct drm_connector *connector;
99 int ret;
100
101 lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL);
102 if (lvdscon == NULL)
103 return -ENOMEM;
104
105 lvdscon->panel = panel;
106
107 connector = &lvdscon->connector.connector;
108 connector->display_info.width_mm = panel->width_mm;
109 connector->display_info.height_mm = panel->height_mm;
110
111 ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
112 DRM_MODE_CONNECTOR_LVDS);
113 if (ret < 0)
114 return ret;
115
116 drm_connector_helper_add(connector, &connector_helper_funcs);
117 ret = drm_sysfs_connector_add(connector);
118 if (ret < 0)
119 return ret;
120
121 drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
122 drm_object_property_set_value(&connector->base,
123 rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
124
125 ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
126 if (ret < 0)
127 return ret;
128
129 connector->encoder = &renc->encoder;
130 lvdscon->connector.encoder = renc;
131
132 return 0;
133}
134
135/* -----------------------------------------------------------------------------
136 * Encoder
137 */
138
139static void rcar_du_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
140{
141}
142
143static bool rcar_du_lvds_encoder_mode_fixup(struct drm_encoder *encoder,
144 const struct drm_display_mode *mode,
145 struct drm_display_mode *adjusted_mode)
146{
147 const struct drm_display_mode *panel_mode;
148 struct drm_device *dev = encoder->dev;
149 struct drm_connector *connector;
150 bool found = false;
151
152 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
153 if (connector->encoder == encoder) {
154 found = true;
155 break;
156 }
157 }
158
159 if (!found) {
160 dev_dbg(dev->dev, "mode_fixup: no connector found\n");
161 return false;
162 }
163
164 if (list_empty(&connector->modes)) {
165 dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
166 return false;
167 }
168
169 panel_mode = list_first_entry(&connector->modes,
170 struct drm_display_mode, head);
171
172 /* We're not allowed to modify the resolution. */
173 if (mode->hdisplay != panel_mode->hdisplay ||
174 mode->vdisplay != panel_mode->vdisplay)
175 return false;
176
177 /* The flat panel mode is fixed, just copy it to the adjusted mode. */
178 drm_mode_copy(adjusted_mode, panel_mode);
179
180 return true;
181}
182
183static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
184 .dpms = rcar_du_lvds_encoder_dpms,
185 .mode_fixup = rcar_du_lvds_encoder_mode_fixup,
186 .prepare = rcar_du_encoder_mode_prepare,
187 .commit = rcar_du_encoder_mode_commit,
188 .mode_set = rcar_du_encoder_mode_set,
189};
190
191static const struct drm_encoder_funcs encoder_funcs = {
192 .destroy = drm_encoder_cleanup,
193};
194
195int rcar_du_lvds_init(struct rcar_du_device *rcdu,
196 const struct rcar_du_encoder_lvds_data *data,
197 unsigned int output)
198{
199 struct rcar_du_encoder *renc;
200 int ret;
201
202 renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
203 if (renc == NULL)
204 return -ENOMEM;
205
206 renc->output = output;
207
208 ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
209 DRM_MODE_ENCODER_LVDS);
210 if (ret < 0)
211 return ret;
212
213 drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
214
215 return rcar_du_lvds_connector_init(rcdu, renc, &data->panel);
216}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.h b/drivers/gpu/drm/rcar-du/rcar_du_lvds.h
new file mode 100644
index 000000000000..b47f8328e103
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvds.h
@@ -0,0 +1,24 @@
1/*
2 * rcar_du_lvds.h -- R-Car Display Unit LVDS Encoder and Connector
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __RCAR_DU_LVDS_H__
15#define __RCAR_DU_LVDS_H__
16
17struct rcar_du_device;
18struct rcar_du_encoder_lvds_data;
19
20int rcar_du_lvds_init(struct rcar_du_device *rcdu,
21 const struct rcar_du_encoder_lvds_data *data,
22 unsigned int output);
23
24#endif /* __RCAR_DU_LVDS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
new file mode 100644
index 000000000000..a65f81ddf51d
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
@@ -0,0 +1,507 @@
1/*
2 * rcar_du_plane.c -- R-Car Display Unit Planes
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <drm/drmP.h>
15#include <drm/drm_crtc.h>
16#include <drm/drm_crtc_helper.h>
17#include <drm/drm_fb_cma_helper.h>
18#include <drm/drm_gem_cma_helper.h>
19
20#include "rcar_du_drv.h"
21#include "rcar_du_kms.h"
22#include "rcar_du_plane.h"
23#include "rcar_du_regs.h"
24
25#define RCAR_DU_COLORKEY_NONE (0 << 24)
26#define RCAR_DU_COLORKEY_SOURCE (1 << 24)
27#define RCAR_DU_COLORKEY_MASK (1 << 24)
28
29struct rcar_du_kms_plane {
30 struct drm_plane plane;
31 struct rcar_du_plane *hwplane;
32};
33
34static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
35{
36 return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane;
37}
38
39static u32 rcar_du_plane_read(struct rcar_du_device *rcdu,
40 unsigned int index, u32 reg)
41{
42 return rcar_du_read(rcdu, index * PLANE_OFF + reg);
43}
44
45static void rcar_du_plane_write(struct rcar_du_device *rcdu,
46 unsigned int index, u32 reg, u32 data)
47{
48 rcar_du_write(rcdu, index * PLANE_OFF + reg, data);
49}
50
51int rcar_du_plane_reserve(struct rcar_du_plane *plane,
52 const struct rcar_du_format_info *format)
53{
54 struct rcar_du_device *rcdu = plane->dev;
55 unsigned int i;
56 int ret = -EBUSY;
57
58 mutex_lock(&rcdu->planes.lock);
59
60 for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
61 if (!(rcdu->planes.free & (1 << i)))
62 continue;
63
64 if (format->planes == 1 ||
65 rcdu->planes.free & (1 << ((i + 1) % 8)))
66 break;
67 }
68
69 if (i == ARRAY_SIZE(rcdu->planes.planes))
70 goto done;
71
72 rcdu->planes.free &= ~(1 << i);
73 if (format->planes == 2)
74 rcdu->planes.free &= ~(1 << ((i + 1) % 8));
75
76 plane->hwindex = i;
77
78 ret = 0;
79
80done:
81 mutex_unlock(&rcdu->planes.lock);
82 return ret;
83}
84
85void rcar_du_plane_release(struct rcar_du_plane *plane)
86{
87 struct rcar_du_device *rcdu = plane->dev;
88
89 if (plane->hwindex == -1)
90 return;
91
92 mutex_lock(&rcdu->planes.lock);
93 rcdu->planes.free |= 1 << plane->hwindex;
94 if (plane->format->planes == 2)
95 rcdu->planes.free |= 1 << ((plane->hwindex + 1) % 8);
96 mutex_unlock(&rcdu->planes.lock);
97
98 plane->hwindex = -1;
99}
100
101void rcar_du_plane_update_base(struct rcar_du_plane *plane)
102{
103 struct rcar_du_device *rcdu = plane->dev;
104 unsigned int index = plane->hwindex;
105
106 /* According to the datasheet the Y position is expressed in raster line
107 * units. However, 32bpp formats seem to require a doubled Y position
108 * value. Similarly, for the second plane, NV12 and NV21 formats seem to
109 * require a halved Y position value.
110 */
111 rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
112 rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y *
113 (plane->format->bpp == 32 ? 2 : 1));
114 rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[0]);
115
116 if (plane->format->planes == 2) {
117 index = (index + 1) % 8;
118
119 rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
120 rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y *
121 (plane->format->bpp == 16 ? 2 : 1) / 2);
122 rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[1]);
123 }
124}
125
126void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
127 struct drm_framebuffer *fb)
128{
129 struct drm_gem_cma_object *gem;
130
131 gem = drm_fb_cma_get_gem_obj(fb, 0);
132 plane->dma[0] = gem->paddr + fb->offsets[0];
133
134 if (plane->format->planes == 2) {
135 gem = drm_fb_cma_get_gem_obj(fb, 1);
136 plane->dma[1] = gem->paddr + fb->offsets[1];
137 }
138}
139
140static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
141 unsigned int index)
142{
143 struct rcar_du_device *rcdu = plane->dev;
144 u32 colorkey;
145 u32 pnmr;
146
147 /* The PnALPHAR register controls alpha-blending in 16bpp formats
148 * (ARGB1555 and XRGB1555).
149 *
150 * For ARGB, set the alpha value to 0, and enable alpha-blending when
151 * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
152 *
153 * For XRGB, set the alpha value to the plane-wide alpha value and
154 * enable alpha-blending regardless of the X bit value.
155 */
156 if (plane->format->fourcc != DRM_FORMAT_XRGB1555)
157 rcar_du_plane_write(rcdu, index, PnALPHAR, PnALPHAR_ABIT_0);
158 else
159 rcar_du_plane_write(rcdu, index, PnALPHAR,
160 PnALPHAR_ABIT_X | plane->alpha);
161
162 pnmr = PnMR_BM_MD | plane->format->pnmr;
163
164 /* Disable color keying when requested. YUV formats have the
165 * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
166 * automatically.
167 */
168 if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
169 pnmr |= PnMR_SPIM_TP_OFF;
170
171 /* For packed YUV formats we need to select the U/V order. */
172 if (plane->format->fourcc == DRM_FORMAT_YUYV)
173 pnmr |= PnMR_YCDF_YUYV;
174
175 rcar_du_plane_write(rcdu, index, PnMR, pnmr);
176
177 switch (plane->format->fourcc) {
178 case DRM_FORMAT_RGB565:
179 colorkey = ((plane->colorkey & 0xf80000) >> 8)
180 | ((plane->colorkey & 0x00fc00) >> 5)
181 | ((plane->colorkey & 0x0000f8) >> 3);
182 rcar_du_plane_write(rcdu, index, PnTC2R, colorkey);
183 break;
184
185 case DRM_FORMAT_ARGB1555:
186 case DRM_FORMAT_XRGB1555:
187 colorkey = ((plane->colorkey & 0xf80000) >> 9)
188 | ((plane->colorkey & 0x00f800) >> 6)
189 | ((plane->colorkey & 0x0000f8) >> 3);
190 rcar_du_plane_write(rcdu, index, PnTC2R, colorkey);
191 break;
192
193 case DRM_FORMAT_XRGB8888:
194 case DRM_FORMAT_ARGB8888:
195 rcar_du_plane_write(rcdu, index, PnTC3R,
196 PnTC3R_CODE | (plane->colorkey & 0xffffff));
197 break;
198 }
199}
200
201static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
202 unsigned int index)
203{
204 struct rcar_du_device *rcdu = plane->dev;
205 u32 ddcr2 = PnDDCR2_CODE;
206 u32 ddcr4;
207 u32 mwr;
208
209 /* Data format
210 *
211 * The data format is selected by the DDDF field in PnMR and the EDF
212 * field in DDCR4.
213 */
214 ddcr4 = rcar_du_plane_read(rcdu, index, PnDDCR4);
215 ddcr4 &= ~PnDDCR4_EDF_MASK;
216 ddcr4 |= plane->format->edf | PnDDCR4_CODE;
217
218 rcar_du_plane_setup_mode(plane, index);
219
220 if (plane->format->planes == 2) {
221 if (plane->hwindex != index) {
222 if (plane->format->fourcc == DRM_FORMAT_NV12 ||
223 plane->format->fourcc == DRM_FORMAT_NV21)
224 ddcr2 |= PnDDCR2_Y420;
225
226 if (plane->format->fourcc == DRM_FORMAT_NV21)
227 ddcr2 |= PnDDCR2_NV21;
228
229 ddcr2 |= PnDDCR2_DIVU;
230 } else {
231 ddcr2 |= PnDDCR2_DIVY;
232 }
233 }
234
235 rcar_du_plane_write(rcdu, index, PnDDCR2, ddcr2);
236 rcar_du_plane_write(rcdu, index, PnDDCR4, ddcr4);
237
238 /* Memory pitch (expressed in pixels) */
239 if (plane->format->planes == 2)
240 mwr = plane->pitch;
241 else
242 mwr = plane->pitch * 8 / plane->format->bpp;
243
244 rcar_du_plane_write(rcdu, index, PnMWR, mwr);
245
246 /* Destination position and size */
247 rcar_du_plane_write(rcdu, index, PnDSXR, plane->width);
248 rcar_du_plane_write(rcdu, index, PnDSYR, plane->height);
249 rcar_du_plane_write(rcdu, index, PnDPXR, plane->dst_x);
250 rcar_du_plane_write(rcdu, index, PnDPYR, plane->dst_y);
251
252 /* Wrap-around and blinking, disabled */
253 rcar_du_plane_write(rcdu, index, PnWASPR, 0);
254 rcar_du_plane_write(rcdu, index, PnWAMWR, 4095);
255 rcar_du_plane_write(rcdu, index, PnBTR, 0);
256 rcar_du_plane_write(rcdu, index, PnMLR, 0);
257}
258
259void rcar_du_plane_setup(struct rcar_du_plane *plane)
260{
261 __rcar_du_plane_setup(plane, plane->hwindex);
262 if (plane->format->planes == 2)
263 __rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8);
264
265 rcar_du_plane_update_base(plane);
266}
267
268static int
269rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
270 struct drm_framebuffer *fb, int crtc_x, int crtc_y,
271 unsigned int crtc_w, unsigned int crtc_h,
272 uint32_t src_x, uint32_t src_y,
273 uint32_t src_w, uint32_t src_h)
274{
275 struct rcar_du_plane *rplane = to_rcar_plane(plane);
276 struct rcar_du_device *rcdu = plane->dev->dev_private;
277 const struct rcar_du_format_info *format;
278 unsigned int nplanes;
279 int ret;
280
281 format = rcar_du_format_info(fb->pixel_format);
282 if (format == NULL) {
283 dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
284 fb->pixel_format);
285 return -EINVAL;
286 }
287
288 if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
289 dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
290 return -EINVAL;
291 }
292
293 nplanes = rplane->format ? rplane->format->planes : 0;
294
295 /* Reallocate hardware planes if the number of required planes has
296 * changed.
297 */
298 if (format->planes != nplanes) {
299 rcar_du_plane_release(rplane);
300 ret = rcar_du_plane_reserve(rplane, format);
301 if (ret < 0)
302 return ret;
303 }
304
305 rplane->crtc = crtc;
306 rplane->format = format;
307 rplane->pitch = fb->pitches[0];
308
309 rplane->src_x = src_x >> 16;
310 rplane->src_y = src_y >> 16;
311 rplane->dst_x = crtc_x;
312 rplane->dst_y = crtc_y;
313 rplane->width = crtc_w;
314 rplane->height = crtc_h;
315
316 rcar_du_plane_compute_base(rplane, fb);
317 rcar_du_plane_setup(rplane);
318
319 mutex_lock(&rcdu->planes.lock);
320 rplane->enabled = true;
321 rcar_du_crtc_update_planes(rplane->crtc);
322 mutex_unlock(&rcdu->planes.lock);
323
324 return 0;
325}
326
327static int rcar_du_plane_disable(struct drm_plane *plane)
328{
329 struct rcar_du_device *rcdu = plane->dev->dev_private;
330 struct rcar_du_plane *rplane = to_rcar_plane(plane);
331
332 if (!rplane->enabled)
333 return 0;
334
335 mutex_lock(&rcdu->planes.lock);
336 rplane->enabled = false;
337 rcar_du_crtc_update_planes(rplane->crtc);
338 mutex_unlock(&rcdu->planes.lock);
339
340 rcar_du_plane_release(rplane);
341
342 rplane->crtc = NULL;
343 rplane->format = NULL;
344
345 return 0;
346}
347
348/* Both the .set_property and the .update_plane operations are called with the
349 * mode_config lock held. There is this no need to explicitly protect access to
350 * the alpha and colorkey fields and the mode register.
351 */
352static void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha)
353{
354 if (plane->alpha == alpha)
355 return;
356
357 plane->alpha = alpha;
358 if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555)
359 return;
360
361 rcar_du_plane_setup_mode(plane, plane->hwindex);
362}
363
364static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane,
365 u32 colorkey)
366{
367 if (plane->colorkey == colorkey)
368 return;
369
370 plane->colorkey = colorkey;
371 if (!plane->enabled)
372 return;
373
374 rcar_du_plane_setup_mode(plane, plane->hwindex);
375}
376
377static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane,
378 unsigned int zpos)
379{
380 struct rcar_du_device *rcdu = plane->dev;
381
382 mutex_lock(&rcdu->planes.lock);
383 if (plane->zpos == zpos)
384 goto done;
385
386 plane->zpos = zpos;
387 if (!plane->enabled)
388 goto done;
389
390 rcar_du_crtc_update_planes(plane->crtc);
391
392done:
393 mutex_unlock(&rcdu->planes.lock);
394}
395
396static int rcar_du_plane_set_property(struct drm_plane *plane,
397 struct drm_property *property,
398 uint64_t value)
399{
400 struct rcar_du_device *rcdu = plane->dev->dev_private;
401 struct rcar_du_plane *rplane = to_rcar_plane(plane);
402
403 if (property == rcdu->planes.alpha)
404 rcar_du_plane_set_alpha(rplane, value);
405 else if (property == rcdu->planes.colorkey)
406 rcar_du_plane_set_colorkey(rplane, value);
407 else if (property == rcdu->planes.zpos)
408 rcar_du_plane_set_zpos(rplane, value);
409 else
410 return -EINVAL;
411
412 return 0;
413}
414
415static const struct drm_plane_funcs rcar_du_plane_funcs = {
416 .update_plane = rcar_du_plane_update,
417 .disable_plane = rcar_du_plane_disable,
418 .set_property = rcar_du_plane_set_property,
419 .destroy = drm_plane_cleanup,
420};
421
422static const uint32_t formats[] = {
423 DRM_FORMAT_RGB565,
424 DRM_FORMAT_ARGB1555,
425 DRM_FORMAT_XRGB1555,
426 DRM_FORMAT_XRGB8888,
427 DRM_FORMAT_ARGB8888,
428 DRM_FORMAT_UYVY,
429 DRM_FORMAT_YUYV,
430 DRM_FORMAT_NV12,
431 DRM_FORMAT_NV21,
432 DRM_FORMAT_NV16,
433};
434
435int rcar_du_plane_init(struct rcar_du_device *rcdu)
436{
437 unsigned int i;
438
439 mutex_init(&rcdu->planes.lock);
440 rcdu->planes.free = 0xff;
441
442 rcdu->planes.alpha =
443 drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
444 if (rcdu->planes.alpha == NULL)
445 return -ENOMEM;
446
447 /* The color key is expressed as an RGB888 triplet stored in a 32-bit
448 * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
449 * or enable source color keying (1).
450 */
451 rcdu->planes.colorkey =
452 drm_property_create_range(rcdu->ddev, 0, "colorkey",
453 0, 0x01ffffff);
454 if (rcdu->planes.colorkey == NULL)
455 return -ENOMEM;
456
457 rcdu->planes.zpos =
458 drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7);
459 if (rcdu->planes.zpos == NULL)
460 return -ENOMEM;
461
462 for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
463 struct rcar_du_plane *plane = &rcdu->planes.planes[i];
464
465 plane->dev = rcdu;
466 plane->hwindex = -1;
467 plane->alpha = 255;
468 plane->colorkey = RCAR_DU_COLORKEY_NONE;
469 plane->zpos = 0;
470 }
471
472 return 0;
473}
474
475int rcar_du_plane_register(struct rcar_du_device *rcdu)
476{
477 unsigned int i;
478 int ret;
479
480 for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) {
481 struct rcar_du_kms_plane *plane;
482
483 plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL);
484 if (plane == NULL)
485 return -ENOMEM;
486
487 plane->hwplane = &rcdu->planes.planes[i + 2];
488 plane->hwplane->zpos = 1;
489
490 ret = drm_plane_init(rcdu->ddev, &plane->plane,
491 (1 << rcdu->num_crtcs) - 1,
492 &rcar_du_plane_funcs, formats,
493 ARRAY_SIZE(formats), false);
494 if (ret < 0)
495 return ret;
496
497 drm_object_attach_property(&plane->plane.base,
498 rcdu->planes.alpha, 255);
499 drm_object_attach_property(&plane->plane.base,
500 rcdu->planes.colorkey,
501 RCAR_DU_COLORKEY_NONE);
502 drm_object_attach_property(&plane->plane.base,
503 rcdu->planes.zpos, 1);
504 }
505
506 return 0;
507}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
new file mode 100644
index 000000000000..5397dba2fe57
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
@@ -0,0 +1,67 @@
1/*
2 * rcar_du_plane.h -- R-Car Display Unit Planes
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __RCAR_DU_PLANE_H__
15#define __RCAR_DU_PLANE_H__
16
17struct drm_crtc;
18struct drm_framebuffer;
19struct rcar_du_device;
20struct rcar_du_format_info;
21
22/* The RCAR DU has 8 hardware planes, shared between KMS planes and CRTCs. As
23 * using KMS planes requires at least one of the CRTCs being enabled, no more
24 * than 7 KMS planes can be available. We thus create 7 KMS planes and
25 * 9 software planes (one for each KMS planes and one for each CRTC).
26 */
27
28#define RCAR_DU_NUM_KMS_PLANES 7
29#define RCAR_DU_NUM_HW_PLANES 8
30#define RCAR_DU_NUM_SW_PLANES 9
31
32struct rcar_du_plane {
33 struct rcar_du_device *dev;
34 struct drm_crtc *crtc;
35
36 bool enabled;
37
38 int hwindex; /* 0-based, -1 means unused */
39 unsigned int alpha;
40 unsigned int colorkey;
41 unsigned int zpos;
42
43 const struct rcar_du_format_info *format;
44
45 unsigned long dma[2];
46 unsigned int pitch;
47
48 unsigned int width;
49 unsigned int height;
50
51 unsigned int src_x;
52 unsigned int src_y;
53 unsigned int dst_x;
54 unsigned int dst_y;
55};
56
57int rcar_du_plane_init(struct rcar_du_device *rcdu);
58int rcar_du_plane_register(struct rcar_du_device *rcdu);
59void rcar_du_plane_setup(struct rcar_du_plane *plane);
60void rcar_du_plane_update_base(struct rcar_du_plane *plane);
61void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
62 struct drm_framebuffer *fb);
63int rcar_du_plane_reserve(struct rcar_du_plane *plane,
64 const struct rcar_du_format_info *format);
65void rcar_du_plane_release(struct rcar_du_plane *plane);
66
67#endif /* __RCAR_DU_PLANE_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
new file mode 100644
index 000000000000..69f21f19b51c
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
@@ -0,0 +1,445 @@
1/*
2 * rcar_du_regs.h -- R-Car Display Unit Registers Definitions
3 *
4 * Copyright (C) 2013 Renesas Electronics Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 */
12
13#ifndef __RCAR_DU_REGS_H__
14#define __RCAR_DU_REGS_H__
15
16#define DISP2_REG_OFFSET 0x30000
17
18/* -----------------------------------------------------------------------------
19 * Display Control Registers
20 */
21
22#define DSYSR 0x00000 /* display 1 */
23#define D2SYSR 0x30000 /* display 2 */
24#define DSYSR_ILTS (1 << 29)
25#define DSYSR_DSEC (1 << 20)
26#define DSYSR_IUPD (1 << 16)
27#define DSYSR_DRES (1 << 9)
28#define DSYSR_DEN (1 << 8)
29#define DSYSR_TVM_MASTER (0 << 6)
30#define DSYSR_TVM_SWITCH (1 << 6)
31#define DSYSR_TVM_TVSYNC (2 << 6)
32#define DSYSR_TVM_MASK (3 << 6)
33#define DSYSR_SCM_INT_NONE (0 << 4)
34#define DSYSR_SCM_INT_SYNC (2 << 4)
35#define DSYSR_SCM_INT_VIDEO (3 << 4)
36
37#define DSMR 0x00004
38#define D2SMR 0x30004
39#define DSMR_VSPM (1 << 28)
40#define DSMR_ODPM (1 << 27)
41#define DSMR_DIPM_DISP (0 << 25)
42#define DSMR_DIPM_CSYNC (1 << 25)
43#define DSMR_DIPM_DE (3 << 25)
44#define DSMR_DIPM_MASK (3 << 25)
45#define DSMR_CSPM (1 << 24)
46#define DSMR_DIL (1 << 19)
47#define DSMR_VSL (1 << 18)
48#define DSMR_HSL (1 << 17)
49#define DSMR_DDIS (1 << 16)
50#define DSMR_CDEL (1 << 15)
51#define DSMR_CDEM_CDE (0 << 13)
52#define DSMR_CDEM_LOW (2 << 13)
53#define DSMR_CDEM_HIGH (3 << 13)
54#define DSMR_CDEM_MASK (3 << 13)
55#define DSMR_CDED (1 << 12)
56#define DSMR_ODEV (1 << 8)
57#define DSMR_CSY_VH_OR (0 << 6)
58#define DSMR_CSY_333 (2 << 6)
59#define DSMR_CSY_222 (3 << 6)
60#define DSMR_CSY_MASK (3 << 6)
61
62#define DSSR 0x00008
63#define D2SSR 0x30008
64#define DSSR_VC1FB_DSA0 (0 << 30)
65#define DSSR_VC1FB_DSA1 (1 << 30)
66#define DSSR_VC1FB_DSA2 (2 << 30)
67#define DSSR_VC1FB_INIT (3 << 30)
68#define DSSR_VC1FB_MASK (3 << 30)
69#define DSSR_VC0FB_DSA0 (0 << 28)
70#define DSSR_VC0FB_DSA1 (1 << 28)
71#define DSSR_VC0FB_DSA2 (2 << 28)
72#define DSSR_VC0FB_INIT (3 << 28)
73#define DSSR_VC0FB_MASK (3 << 28)
74#define DSSR_DFB(n) (1 << ((n)+15))
75#define DSSR_TVR (1 << 15)
76#define DSSR_FRM (1 << 14)
77#define DSSR_VBK (1 << 11)
78#define DSSR_RINT (1 << 9)
79#define DSSR_HBK (1 << 8)
80#define DSSR_ADC(n) (1 << ((n)-1))
81
82#define DSRCR 0x0000c
83#define D2SRCR 0x3000c
84#define DSRCR_TVCL (1 << 15)
85#define DSRCR_FRCL (1 << 14)
86#define DSRCR_VBCL (1 << 11)
87#define DSRCR_RICL (1 << 9)
88#define DSRCR_HBCL (1 << 8)
89#define DSRCR_ADCL(n) (1 << ((n)-1))
90#define DSRCR_MASK 0x0000cbff
91
92#define DIER 0x00010
93#define D2IER 0x30010
94#define DIER_TVE (1 << 15)
95#define DIER_FRE (1 << 14)
96#define DIER_VBE (1 << 11)
97#define DIER_RIE (1 << 9)
98#define DIER_HBE (1 << 8)
99#define DIER_ADCE(n) (1 << ((n)-1))
100
101#define CPCR 0x00014
102#define CPCR_CP4CE (1 << 19)
103#define CPCR_CP3CE (1 << 18)
104#define CPCR_CP2CE (1 << 17)
105#define CPCR_CP1CE (1 << 16)
106
107#define DPPR 0x00018
108#define DPPR_DPE(n) (1 << ((n)*4-1))
109#define DPPR_DPS(n, p) (((p)-1) << DPPR_DPS_SHIFT(n))
110#define DPPR_DPS_SHIFT(n) (((n)-1)*4)
111#define DPPR_BPP16 (DPPR_DPE(8) | DPPR_DPS(8, 1)) /* plane1 */
112#define DPPR_BPP32_P1 (DPPR_DPE(7) | DPPR_DPS(7, 1))
113#define DPPR_BPP32_P2 (DPPR_DPE(8) | DPPR_DPS(8, 2))
114#define DPPR_BPP32 (DPPR_BPP32_P1 | DPPR_BPP32_P2) /* plane1 & 2 */
115
116#define DEFR 0x00020
117#define D2EFR 0x30020
118#define DEFR_CODE (0x7773 << 16)
119#define DEFR_EXSL (1 << 12)
120#define DEFR_EXVL (1 << 11)
121#define DEFR_EXUP (1 << 5)
122#define DEFR_VCUP (1 << 4)
123#define DEFR_DEFE (1 << 0)
124
125#define DAPCR 0x00024
126#define DAPCR_CODE (0x7773 << 16)
127#define DAPCR_AP2E (1 << 4)
128#define DAPCR_AP1E (1 << 0)
129
130#define DCPCR 0x00028
131#define DCPCR_CODE (0x7773 << 16)
132#define DCPCR_CA2B (1 << 13)
133#define DCPCR_CD2F (1 << 12)
134#define DCPCR_DC2E (1 << 8)
135#define DCPCR_CAB (1 << 5)
136#define DCPCR_CDF (1 << 4)
137#define DCPCR_DCE (1 << 0)
138
139#define DEFR2 0x00034
140#define D2EFR2 0x30034
141#define DEFR2_CODE (0x7775 << 16)
142#define DEFR2_DEFE2G (1 << 0)
143
144#define DEFR3 0x00038
145#define D2EFR3 0x30038
146#define DEFR3_CODE (0x7776 << 16)
147#define DEFR3_EVDA (1 << 14)
148#define DEFR3_EVDM_1 (1 << 12)
149#define DEFR3_EVDM_2 (2 << 12)
150#define DEFR3_EVDM_3 (3 << 12)
151#define DEFR3_VMSM2_EMA (1 << 6)
152#define DEFR3_VMSM1_ENA (1 << 4)
153#define DEFR3_DEFE3 (1 << 0)
154
155#define DEFR4 0x0003c
156#define D2EFR4 0x3003c
157#define DEFR4_CODE (0x7777 << 16)
158#define DEFR4_LRUO (1 << 5)
159#define DEFR4_SPCE (1 << 4)
160
161#define DVCSR 0x000d0
162#define DVCSR_VCnFB2_DSA0(n) (0 << ((n)*2+16))
163#define DVCSR_VCnFB2_DSA1(n) (1 << ((n)*2+16))
164#define DVCSR_VCnFB2_DSA2(n) (2 << ((n)*2+16))
165#define DVCSR_VCnFB2_INIT(n) (3 << ((n)*2+16))
166#define DVCSR_VCnFB2_MASK(n) (3 << ((n)*2+16))
167#define DVCSR_VCnFB_DSA0(n) (0 << ((n)*2))
168#define DVCSR_VCnFB_DSA1(n) (1 << ((n)*2))
169#define DVCSR_VCnFB_DSA2(n) (2 << ((n)*2))
170#define DVCSR_VCnFB_INIT(n) (3 << ((n)*2))
171#define DVCSR_VCnFB_MASK(n) (3 << ((n)*2))
172
173#define DEFR5 0x000e0
174#define DEFR5_CODE (0x66 << 24)
175#define DEFR5_YCRGB2_DIS (0 << 14)
176#define DEFR5_YCRGB2_PRI1 (1 << 14)
177#define DEFR5_YCRGB2_PRI2 (2 << 14)
178#define DEFR5_YCRGB2_PRI3 (3 << 14)
179#define DEFR5_YCRGB2_MASK (3 << 14)
180#define DEFR5_YCRGB1_DIS (0 << 12)
181#define DEFR5_YCRGB1_PRI1 (1 << 12)
182#define DEFR5_YCRGB1_PRI2 (2 << 12)
183#define DEFR5_YCRGB1_PRI3 (3 << 12)
184#define DEFR5_YCRGB1_MASK (3 << 12)
185#define DEFR5_DEFE5 (1 << 0)
186
187#define DDLTR 0x000e4
188#define DDLTR_CODE (0x7766 << 16)
189#define DDLTR_DLAR2 (1 << 6)
190#define DDLTR_DLAY2 (1 << 5)
191#define DDLTR_DLAY1 (1 << 1)
192
193#define DEFR6 0x000e8
194#define DEFR6_CODE (0x7778 << 16)
195#define DEFR6_ODPM22_D2SMR (0 << 10)
196#define DEFR6_ODPM22_DISP (2 << 10)
197#define DEFR6_ODPM22_CDE (3 << 10)
198#define DEFR6_ODPM22_MASK (3 << 10)
199#define DEFR6_ODPM12_DSMR (0 << 8)
200#define DEFR6_ODPM12_DISP (2 << 8)
201#define DEFR6_ODPM12_CDE (3 << 8)
202#define DEFR6_ODPM12_MASK (3 << 8)
203#define DEFR6_TCNE2 (1 << 6)
204#define DEFR6_MLOS1 (1 << 2)
205#define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE2)
206
207/* -----------------------------------------------------------------------------
208 * Display Timing Generation Registers
209 */
210
211#define HDSR 0x00040
212#define HDER 0x00044
213#define VDSR 0x00048
214#define VDER 0x0004c
215#define HCR 0x00050
216#define HSWR 0x00054
217#define VCR 0x00058
218#define VSPR 0x0005c
219#define EQWR 0x00060
220#define SPWR 0x00064
221#define CLAMPSR 0x00070
222#define CLAMPWR 0x00074
223#define DESR 0x00078
224#define DEWR 0x0007c
225
226/* -----------------------------------------------------------------------------
227 * Display Attribute Registers
228 */
229
230#define CP1TR 0x00080
231#define CP2TR 0x00084
232#define CP3TR 0x00088
233#define CP4TR 0x0008c
234
235#define DOOR 0x00090
236#define DOOR_RGB(r, g, b) (((r) << 18) | ((g) << 10) | ((b) << 2))
237#define CDER 0x00094
238#define CDER_RGB(r, g, b) (((r) << 18) | ((g) << 10) | ((b) << 2))
239#define BPOR 0x00098
240#define BPOR_RGB(r, g, b) (((r) << 18) | ((g) << 10) | ((b) << 2))
241
242#define RINTOFSR 0x0009c
243
244#define DSHPR 0x000c8
245#define DSHPR_CODE (0x7776 << 16)
246#define DSHPR_PRIH (0xa << 4)
247#define DSHPR_PRIL_BPP16 (0x8 << 0)
248#define DSHPR_PRIL_BPP32 (0x9 << 0)
249
250/* -----------------------------------------------------------------------------
251 * Display Plane Registers
252 */
253
254#define PLANE_OFF 0x00100
255
256#define PnMR 0x00100 /* plane 1 */
257#define PnMR_VISL_VIN0 (0 << 26) /* use Video Input 0 */
258#define PnMR_VISL_VIN1 (1 << 26) /* use Video Input 1 */
259#define PnMR_VISL_VIN2 (2 << 26) /* use Video Input 2 */
260#define PnMR_VISL_VIN3 (3 << 26) /* use Video Input 3 */
261#define PnMR_YCDF_YUYV (1 << 20) /* YUYV format */
262#define PnMR_TC_R (0 << 17) /* Tranparent color is PnTC1R */
263#define PnMR_TC_CP (1 << 17) /* Tranparent color is color palette */
264#define PnMR_WAE (1 << 16) /* Wrap around Enable */
265#define PnMR_SPIM_TP (0 << 12) /* Transparent Color */
266#define PnMR_SPIM_ALP (1 << 12) /* Alpha Blending */
267#define PnMR_SPIM_EOR (2 << 12) /* EOR */
268#define PnMR_SPIM_TP_OFF (1 << 14) /* No Transparent Color */
269#define PnMR_CPSL_CP1 (0 << 8) /* Color Palette selected 1 */
270#define PnMR_CPSL_CP2 (1 << 8) /* Color Palette selected 2 */
271#define PnMR_CPSL_CP3 (2 << 8) /* Color Palette selected 3 */
272#define PnMR_CPSL_CP4 (3 << 8) /* Color Palette selected 4 */
273#define PnMR_DC (1 << 7) /* Display Area Change */
274#define PnMR_BM_MD (0 << 4) /* Manual Display Change Mode */
275#define PnMR_BM_AR (1 << 4) /* Auto Rendering Mode */
276#define PnMR_BM_AD (2 << 4) /* Auto Display Change Mode */
277#define PnMR_BM_VC (3 << 4) /* Video Capture Mode */
278#define PnMR_DDDF_8BPP (0 << 0) /* 8bit */
279#define PnMR_DDDF_16BPP (1 << 0) /* 16bit or 32bit */
280#define PnMR_DDDF_ARGB (2 << 0) /* ARGB */
281#define PnMR_DDDF_YC (3 << 0) /* YC */
282#define PnMR_DDDF_MASK (3 << 0)
283
284#define PnMWR 0x00104
285
286#define PnALPHAR 0x00108
287#define PnALPHAR_ABIT_1 (0 << 12)
288#define PnALPHAR_ABIT_0 (1 << 12)
289#define PnALPHAR_ABIT_X (2 << 12)
290
291#define PnDSXR 0x00110
292#define PnDSYR 0x00114
293#define PnDPXR 0x00118
294#define PnDPYR 0x0011c
295
296#define PnDSA0R 0x00120
297#define PnDSA1R 0x00124
298#define PnDSA2R 0x00128
299#define PnDSA_MASK 0xfffffff0
300
301#define PnSPXR 0x00130
302#define PnSPYR 0x00134
303#define PnWASPR 0x00138
304#define PnWAMWR 0x0013c
305
306#define PnBTR 0x00140
307
308#define PnTC1R 0x00144
309#define PnTC2R 0x00148
310#define PnTC3R 0x0014c
311#define PnTC3R_CODE (0x66 << 24)
312
313#define PnMLR 0x00150
314
315#define PnSWAPR 0x00180
316#define PnSWAPR_DIGN (1 << 4)
317#define PnSWAPR_SPQW (1 << 3)
318#define PnSWAPR_SPLW (1 << 2)
319#define PnSWAPR_SPWD (1 << 1)
320#define PnSWAPR_SPBY (1 << 0)
321
322#define PnDDCR 0x00184
323#define PnDDCR_CODE (0x7775 << 16)
324#define PnDDCR_LRGB1 (1 << 11)
325#define PnDDCR_LRGB0 (1 << 10)
326
327#define PnDDCR2 0x00188
328#define PnDDCR2_CODE (0x7776 << 16)
329#define PnDDCR2_NV21 (1 << 5)
330#define PnDDCR2_Y420 (1 << 4)
331#define PnDDCR2_DIVU (1 << 1)
332#define PnDDCR2_DIVY (1 << 0)
333
334#define PnDDCR4 0x00190
335#define PnDDCR4_CODE (0x7766 << 16)
336#define PnDDCR4_SDFS_RGB (0 << 4)
337#define PnDDCR4_SDFS_YC (5 << 4)
338#define PnDDCR4_SDFS_MASK (7 << 4)
339#define PnDDCR4_EDF_NONE (0 << 0)
340#define PnDDCR4_EDF_ARGB8888 (1 << 0)
341#define PnDDCR4_EDF_RGB888 (2 << 0)
342#define PnDDCR4_EDF_RGB666 (3 << 0)
343#define PnDDCR4_EDF_MASK (7 << 0)
344
345#define APnMR 0x0a100
346#define APnMR_WAE (1 << 16) /* Wrap around Enable */
347#define APnMR_DC (1 << 7) /* Display Area Change */
348#define APnMR_BM_MD (0 << 4) /* Manual Display Change Mode */
349#define APnMR_BM_AD (2 << 4) /* Auto Display Change Mode */
350
351#define APnMWR 0x0a104
352#define APnDSA0R 0x0a120
353#define APnDSA1R 0x0a124
354#define APnDSA2R 0x0a128
355#define APnMLR 0x0a150
356
357/* -----------------------------------------------------------------------------
358 * Display Capture Registers
359 */
360
361#define DCMWR 0x0c104
362#define DC2MWR 0x0c204
363#define DCSAR 0x0c120
364#define DC2SAR 0x0c220
365#define DCMLR 0x0c150
366#define DC2MLR 0x0c250
367
368/* -----------------------------------------------------------------------------
369 * Color Palette Registers
370 */
371
372#define CP1_000R 0x01000
373#define CP1_255R 0x013fc
374#define CP2_000R 0x02000
375#define CP2_255R 0x023fc
376#define CP3_000R 0x03000
377#define CP3_255R 0x033fc
378#define CP4_000R 0x04000
379#define CP4_255R 0x043fc
380
381/* -----------------------------------------------------------------------------
382 * External Synchronization Control Registers
383 */
384
385#define ESCR 0x10000
386#define ESCR2 0x31000
387#define ESCR_DCLKOINV (1 << 25)
388#define ESCR_DCLKSEL_DCLKIN (0 << 20)
389#define ESCR_DCLKSEL_CLKS (1 << 20)
390#define ESCR_DCLKSEL_MASK (1 << 20)
391#define ESCR_DCLKDIS (1 << 16)
392#define ESCR_SYNCSEL_OFF (0 << 8)
393#define ESCR_SYNCSEL_EXVSYNC (2 << 8)
394#define ESCR_SYNCSEL_EXHSYNC (3 << 8)
395#define ESCR_FRQSEL_MASK (0x3f << 0)
396
397#define OTAR 0x10004
398#define OTAR2 0x31004
399
400/* -----------------------------------------------------------------------------
401 * Dual Display Output Control Registers
402 */
403
404#define DORCR 0x11000
405#define DORCR_PG2T (1 << 30)
406#define DORCR_DK2S (1 << 28)
407#define DORCR_PG2D_DS1 (0 << 24)
408#define DORCR_PG2D_DS2 (1 << 24)
409#define DORCR_PG2D_FIX0 (2 << 24)
410#define DORCR_PG2D_DOOR (3 << 24)
411#define DORCR_PG2D_MASK (3 << 24)
412#define DORCR_DR1D (1 << 21)
413#define DORCR_PG1D_DS1 (0 << 16)
414#define DORCR_PG1D_DS2 (1 << 16)
415#define DORCR_PG1D_FIX0 (2 << 16)
416#define DORCR_PG1D_DOOR (3 << 16)
417#define DORCR_PG1D_MASK (3 << 16)
418#define DORCR_RGPV (1 << 4)
419#define DORCR_DPRS (1 << 0)
420
421#define DPTSR 0x11004
422#define DPTSR_PnDK(n) (1 << ((n) + 16))
423#define DPTSR_PnTS(n) (1 << (n))
424
425#define DAPTSR 0x11008
426#define DAPTSR_APnDK(n) (1 << ((n) + 16))
427#define DAPTSR_APnTS(n) (1 << (n))
428
429#define DS1PR 0x11020
430#define DS2PR 0x11024
431
432/* -----------------------------------------------------------------------------
433 * YC-RGB Conversion Coefficient Registers
434 */
435
436#define YNCR 0x11080
437#define YNOR 0x11084
438#define CRNOR 0x11088
439#define CBNOR 0x1108c
440#define RCRCR 0x11090
441#define GCRCR 0x11094
442#define GCBCR 0x11098
443#define BCBCR 0x1109c
444
445#endif /* __RCAR_DU_REGS_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.c b/drivers/gpu/drm/rcar-du/rcar_du_vga.c
new file mode 100644
index 000000000000..327289ec380d
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vga.c
@@ -0,0 +1,149 @@
1/*
2 * rcar_du_vga.c -- R-Car Display Unit VGA DAC and Connector
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <drm/drmP.h>
15#include <drm/drm_crtc.h>
16#include <drm/drm_crtc_helper.h>
17
18#include "rcar_du_drv.h"
19#include "rcar_du_kms.h"
20#include "rcar_du_vga.h"
21
22/* -----------------------------------------------------------------------------
23 * Connector
24 */
25
26static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
27{
28 return 0;
29}
30
31static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector,
32 struct drm_display_mode *mode)
33{
34 return MODE_OK;
35}
36
37static const struct drm_connector_helper_funcs connector_helper_funcs = {
38 .get_modes = rcar_du_vga_connector_get_modes,
39 .mode_valid = rcar_du_vga_connector_mode_valid,
40 .best_encoder = rcar_du_connector_best_encoder,
41};
42
43static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
44{
45 drm_sysfs_connector_remove(connector);
46 drm_connector_cleanup(connector);
47}
48
49static enum drm_connector_status
50rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
51{
52 return connector_status_unknown;
53}
54
55static const struct drm_connector_funcs connector_funcs = {
56 .dpms = drm_helper_connector_dpms,
57 .detect = rcar_du_vga_connector_detect,
58 .fill_modes = drm_helper_probe_single_connector_modes,
59 .destroy = rcar_du_vga_connector_destroy,
60};
61
62static int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
63 struct rcar_du_encoder *renc)
64{
65 struct rcar_du_connector *rcon;
66 struct drm_connector *connector;
67 int ret;
68
69 rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
70 if (rcon == NULL)
71 return -ENOMEM;
72
73 connector = &rcon->connector;
74 connector->display_info.width_mm = 0;
75 connector->display_info.height_mm = 0;
76
77 ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
78 DRM_MODE_CONNECTOR_VGA);
79 if (ret < 0)
80 return ret;
81
82 drm_connector_helper_add(connector, &connector_helper_funcs);
83 ret = drm_sysfs_connector_add(connector);
84 if (ret < 0)
85 return ret;
86
87 drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
88 drm_object_property_set_value(&connector->base,
89 rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
90
91 ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
92 if (ret < 0)
93 return ret;
94
95 connector->encoder = &renc->encoder;
96 rcon->encoder = renc;
97
98 return 0;
99}
100
101/* -----------------------------------------------------------------------------
102 * Encoder
103 */
104
105static void rcar_du_vga_encoder_dpms(struct drm_encoder *encoder, int mode)
106{
107}
108
109static bool rcar_du_vga_encoder_mode_fixup(struct drm_encoder *encoder,
110 const struct drm_display_mode *mode,
111 struct drm_display_mode *adjusted_mode)
112{
113 return true;
114}
115
116static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
117 .dpms = rcar_du_vga_encoder_dpms,
118 .mode_fixup = rcar_du_vga_encoder_mode_fixup,
119 .prepare = rcar_du_encoder_mode_prepare,
120 .commit = rcar_du_encoder_mode_commit,
121 .mode_set = rcar_du_encoder_mode_set,
122};
123
124static const struct drm_encoder_funcs encoder_funcs = {
125 .destroy = drm_encoder_cleanup,
126};
127
128int rcar_du_vga_init(struct rcar_du_device *rcdu,
129 const struct rcar_du_encoder_vga_data *data,
130 unsigned int output)
131{
132 struct rcar_du_encoder *renc;
133 int ret;
134
135 renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
136 if (renc == NULL)
137 return -ENOMEM;
138
139 renc->output = output;
140
141 ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
142 DRM_MODE_ENCODER_DAC);
143 if (ret < 0)
144 return ret;
145
146 drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
147
148 return rcar_du_vga_connector_init(rcdu, renc);
149}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.h b/drivers/gpu/drm/rcar-du/rcar_du_vga.h
new file mode 100644
index 000000000000..66b4d2d7190d
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vga.h
@@ -0,0 +1,24 @@
1/*
2 * rcar_du_vga.h -- R-Car Display Unit VGA DAC and Connector
3 *
4 * Copyright (C) 2013 Renesas Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __RCAR_DU_VGA_H__
15#define __RCAR_DU_VGA_H__
16
17struct rcar_du_device;
18struct rcar_du_encoder_vga_data;
19
20int rcar_du_vga_init(struct rcar_du_device *rcdu,
21 const struct rcar_du_encoder_vga_data *data,
22 unsigned int output);
23
24#endif /* __RCAR_DU_VGA_H__ */