diff options
-rw-r--r-- | drivers/gpu/drm/Kconfig | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/Kconfig | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/Makefile | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 595 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 50 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_drv.c | 325 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_drv.h | 66 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_kms.c | 245 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_kms.h | 59 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_lvds.c | 216 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_lvds.h | 24 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_plane.c | 507 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_plane.h | 67 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_regs.h | 445 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_vga.c | 149 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_vga.h | 24 | ||||
-rw-r--r-- | include/linux/platform_data/rcar-du.h | 54 |
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 | ||
214 | source "drivers/gpu/drm/cirrus/Kconfig" | 214 | source "drivers/gpu/drm/cirrus/Kconfig" |
215 | 215 | ||
216 | source "drivers/gpu/drm/rcar-du/Kconfig" | ||
217 | |||
216 | source "drivers/gpu/drm/shmobile/Kconfig" | 218 | source "drivers/gpu/drm/shmobile/Kconfig" |
217 | 219 | ||
218 | source "drivers/gpu/drm/omapdrm/Kconfig" | 220 | source "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/ | |||
49 | obj-$(CONFIG_DRM_GMA500) += gma500/ | 49 | obj-$(CONFIG_DRM_GMA500) += gma500/ |
50 | obj-$(CONFIG_DRM_UDL) += udl/ | 50 | obj-$(CONFIG_DRM_UDL) += udl/ |
51 | obj-$(CONFIG_DRM_AST) += ast/ | 51 | obj-$(CONFIG_DRM_AST) += ast/ |
52 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ | ||
52 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ | 53 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ |
53 | obj-$(CONFIG_DRM_OMAP) += omapdrm/ | 54 | obj-$(CONFIG_DRM_OMAP) += omapdrm/ |
54 | obj-$(CONFIG_DRM_TILCDC) += tilcdc/ | 55 | obj-$(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 @@ | |||
1 | config 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 @@ | |||
1 | rcar-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 | |||
8 | obj-$(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 | |||
33 | static 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 | |||
40 | static 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 | |||
47 | static 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 | |||
55 | static 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 | |||
63 | static 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 | |||
72 | static 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 | |||
115 | static 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 | |||
134 | static 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 | |||
141 | static 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 | |||
164 | void 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 | |||
174 | void 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 | |||
245 | static 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 | |||
291 | static 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 | |||
314 | void 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 | |||
322 | void 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 | |||
333 | static 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 | |||
341 | static 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 | |||
360 | static 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 | |||
368 | static 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 | |||
387 | static 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 | |||
424 | error: | ||
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 | |||
433 | static 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 | |||
445 | static 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 | |||
458 | static 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 | |||
466 | static 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 | |||
476 | void 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 | |||
496 | static 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 | |||
517 | static 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 | |||
546 | static 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 | |||
552 | int 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 | |||
574 | void 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 | |||
584 | void 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 | |||
22 | struct rcar_du_device; | ||
23 | struct rcar_du_plane; | ||
24 | |||
25 | struct 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 | |||
39 | int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index); | ||
40 | void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable); | ||
41 | void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc); | ||
42 | void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, | ||
43 | struct drm_file *file); | ||
44 | void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); | ||
45 | void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc); | ||
46 | |||
47 | void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output); | ||
48 | void 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 | */ | ||
45 | int 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 | |||
69 | done: | ||
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 | */ | ||
81 | void 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 | |||
93 | static 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 | |||
105 | static 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 | |||
179 | done: | ||
180 | if (ret) | ||
181 | rcar_du_unload(dev); | ||
182 | |||
183 | return ret; | ||
184 | } | ||
185 | |||
186 | static 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 | |||
195 | static 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 | |||
207 | static 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 | |||
216 | static 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 | |||
223 | static 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 | |||
238 | static 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 | ||
270 | static 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 | |||
280 | static 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 | |||
291 | static 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 | |||
299 | static int rcar_du_probe(struct platform_device *pdev) | ||
300 | { | ||
301 | return drm_platform_init(&rcar_du_driver, pdev); | ||
302 | } | ||
303 | |||
304 | static int rcar_du_remove(struct platform_device *pdev) | ||
305 | { | ||
306 | drm_platform_exit(&rcar_du_driver, pdev); | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static 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 | |||
321 | module_platform_driver(rcar_du_platform_driver); | ||
322 | |||
323 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | ||
324 | MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver"); | ||
325 | MODULE_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 | |||
24 | struct clk; | ||
25 | struct device; | ||
26 | struct drm_device; | ||
27 | |||
28 | struct 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 | |||
53 | int rcar_du_get(struct rcar_du_device *rcdu); | ||
54 | void rcar_du_put(struct rcar_du_device *rcdu); | ||
55 | |||
56 | static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg) | ||
57 | { | ||
58 | return ioread32(rcdu->mmio + reg); | ||
59 | } | ||
60 | |||
61 | static 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 | |||
31 | static 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 | |||
96 | const 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 | |||
112 | struct drm_encoder * | ||
113 | rcar_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 | |||
120 | void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) | ||
121 | { | ||
122 | } | ||
123 | |||
124 | void 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 | |||
133 | void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) | ||
134 | { | ||
135 | } | ||
136 | |||
137 | /* ----------------------------------------------------------------------------- | ||
138 | * Frame buffer | ||
139 | */ | ||
140 | |||
141 | static struct drm_framebuffer * | ||
142 | rcar_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 | |||
171 | static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { | ||
172 | .fb_create = rcar_du_fb_create, | ||
173 | }; | ||
174 | |||
175 | int 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 | |||
21 | struct rcar_du_device; | ||
22 | |||
23 | struct 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 | |||
31 | struct 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 | |||
39 | struct 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 | |||
47 | const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc); | ||
48 | |||
49 | struct drm_encoder * | ||
50 | rcar_du_connector_best_encoder(struct drm_connector *connector); | ||
51 | void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder); | ||
52 | void rcar_du_encoder_mode_set(struct drm_encoder *encoder, | ||
53 | struct drm_display_mode *mode, | ||
54 | struct drm_display_mode *adjusted_mode); | ||
55 | void rcar_du_encoder_mode_commit(struct drm_encoder *encoder); | ||
56 | |||
57 | int 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 | |||
22 | struct 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 | |||
35 | static 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 | |||
62 | static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector, | ||
63 | struct drm_display_mode *mode) | ||
64 | { | ||
65 | return MODE_OK; | ||
66 | } | ||
67 | |||
68 | static 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 | |||
74 | static void rcar_du_lvds_connector_destroy(struct drm_connector *connector) | ||
75 | { | ||
76 | drm_sysfs_connector_remove(connector); | ||
77 | drm_connector_cleanup(connector); | ||
78 | } | ||
79 | |||
80 | static enum drm_connector_status | ||
81 | rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force) | ||
82 | { | ||
83 | return connector_status_connected; | ||
84 | } | ||
85 | |||
86 | static 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 | |||
93 | static 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 | |||
139 | static void rcar_du_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
140 | { | ||
141 | } | ||
142 | |||
143 | static 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 | |||
183 | static 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 | |||
191 | static const struct drm_encoder_funcs encoder_funcs = { | ||
192 | .destroy = drm_encoder_cleanup, | ||
193 | }; | ||
194 | |||
195 | int 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 | |||
17 | struct rcar_du_device; | ||
18 | struct rcar_du_encoder_lvds_data; | ||
19 | |||
20 | int 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 | |||
29 | struct rcar_du_kms_plane { | ||
30 | struct drm_plane plane; | ||
31 | struct rcar_du_plane *hwplane; | ||
32 | }; | ||
33 | |||
34 | static 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 | |||
39 | static 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 | |||
45 | static 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 | |||
51 | int 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 | |||
80 | done: | ||
81 | mutex_unlock(&rcdu->planes.lock); | ||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | void 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 | |||
101 | void 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 | |||
126 | void 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 | |||
140 | static 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 | |||
201 | static 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 | |||
259 | void 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 | |||
268 | static int | ||
269 | rcar_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 | |||
327 | static 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 | */ | ||
352 | static 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 | |||
364 | static 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 | |||
377 | static 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 | |||
392 | done: | ||
393 | mutex_unlock(&rcdu->planes.lock); | ||
394 | } | ||
395 | |||
396 | static 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 | |||
415 | static 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 | |||
422 | static 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 | |||
435 | int 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 | |||
475 | int 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 | |||
17 | struct drm_crtc; | ||
18 | struct drm_framebuffer; | ||
19 | struct rcar_du_device; | ||
20 | struct 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 | |||
32 | struct 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 | |||
57 | int rcar_du_plane_init(struct rcar_du_device *rcdu); | ||
58 | int rcar_du_plane_register(struct rcar_du_device *rcdu); | ||
59 | void rcar_du_plane_setup(struct rcar_du_plane *plane); | ||
60 | void rcar_du_plane_update_base(struct rcar_du_plane *plane); | ||
61 | void rcar_du_plane_compute_base(struct rcar_du_plane *plane, | ||
62 | struct drm_framebuffer *fb); | ||
63 | int rcar_du_plane_reserve(struct rcar_du_plane *plane, | ||
64 | const struct rcar_du_format_info *format); | ||
65 | void 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 | |||
26 | static int rcar_du_vga_connector_get_modes(struct drm_connector *connector) | ||
27 | { | ||
28 | return 0; | ||
29 | } | ||
30 | |||
31 | static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector, | ||
32 | struct drm_display_mode *mode) | ||
33 | { | ||
34 | return MODE_OK; | ||
35 | } | ||
36 | |||
37 | static 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 | |||
43 | static void rcar_du_vga_connector_destroy(struct drm_connector *connector) | ||
44 | { | ||
45 | drm_sysfs_connector_remove(connector); | ||
46 | drm_connector_cleanup(connector); | ||
47 | } | ||
48 | |||
49 | static enum drm_connector_status | ||
50 | rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) | ||
51 | { | ||
52 | return connector_status_unknown; | ||
53 | } | ||
54 | |||
55 | static 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 | |||
62 | static 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 | |||
105 | static void rcar_du_vga_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
106 | { | ||
107 | } | ||
108 | |||
109 | static 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 | |||
116 | static 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 | |||
124 | static const struct drm_encoder_funcs encoder_funcs = { | ||
125 | .destroy = drm_encoder_cleanup, | ||
126 | }; | ||
127 | |||
128 | int 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 | |||
17 | struct rcar_du_device; | ||
18 | struct rcar_du_encoder_vga_data; | ||
19 | |||
20 | int 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 | |||
19 | enum rcar_du_encoder_type { | ||
20 | RCAR_DU_ENCODER_UNUSED = 0, | ||
21 | RCAR_DU_ENCODER_VGA, | ||
22 | RCAR_DU_ENCODER_LVDS, | ||
23 | }; | ||
24 | |||
25 | struct 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 | |||
31 | struct rcar_du_encoder_lvds_data { | ||
32 | struct rcar_du_panel_data panel; | ||
33 | }; | ||
34 | |||
35 | struct rcar_du_encoder_vga_data { | ||
36 | /* TODO: Add DDC information for EDID retrieval */ | ||
37 | }; | ||
38 | |||
39 | struct 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 | |||
49 | struct rcar_du_platform_data { | ||
50 | struct rcar_du_encoder_data *encoders; | ||
51 | unsigned int num_encoders; | ||
52 | }; | ||
53 | |||
54 | #endif /* __RCAR_DU_H__ */ | ||