aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--include/linux/platform_data/rcar-du.h54
18 files changed, 2846 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__ */
diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h
new file mode 100644
index 000000000000..80587fdbba3e
--- /dev/null
+++ b/include/linux/platform_data/rcar-du.h
@@ -0,0 +1,54 @@
1/*
2 * rcar_du.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_H__
15#define __RCAR_DU_H__
16
17#include <drm/drm_mode.h>
18
19enum rcar_du_encoder_type {
20 RCAR_DU_ENCODER_UNUSED = 0,
21 RCAR_DU_ENCODER_VGA,
22 RCAR_DU_ENCODER_LVDS,
23};
24
25struct rcar_du_panel_data {
26 unsigned int width_mm; /* Panel width in mm */
27 unsigned int height_mm; /* Panel height in mm */
28 struct drm_mode_modeinfo mode;
29};
30
31struct rcar_du_encoder_lvds_data {
32 struct rcar_du_panel_data panel;
33};
34
35struct rcar_du_encoder_vga_data {
36 /* TODO: Add DDC information for EDID retrieval */
37};
38
39struct rcar_du_encoder_data {
40 enum rcar_du_encoder_type encoder;
41 unsigned int output;
42
43 union {
44 struct rcar_du_encoder_lvds_data lvds;
45 struct rcar_du_encoder_vga_data vga;
46 } u;
47};
48
49struct rcar_du_platform_data {
50 struct rcar_du_encoder_data *encoders;
51 unsigned int num_encoders;
52};
53
54#endif /* __RCAR_DU_H__ */