blob: 7784a3ba785446a6d6265ab0711074e37518ac6f [file] [log] [blame]
Laurent Pinchart4bf8e192013-06-19 13:54:11 +02001/*
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"
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020026#include "rcar_du_plane.h"
27#include "rcar_du_regs.h"
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020028
29#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc)
30
31static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
32{
Laurent Pinchartcb2025d2013-06-16 21:01:02 +020033 struct rcar_du_device *rcdu = rcrtc->group->dev;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020034
35 return rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
36}
37
38static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data)
39{
Laurent Pinchartcb2025d2013-06-16 21:01:02 +020040 struct rcar_du_device *rcdu = rcrtc->group->dev;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020041
42 rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data);
43}
44
45static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr)
46{
Laurent Pinchartcb2025d2013-06-16 21:01:02 +020047 struct rcar_du_device *rcdu = rcrtc->group->dev;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020048
49 rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
50 rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr);
51}
52
53static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set)
54{
Laurent Pinchartcb2025d2013-06-16 21:01:02 +020055 struct rcar_du_device *rcdu = rcrtc->group->dev;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020056
57 rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
58 rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set);
59}
60
61static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg,
62 u32 clr, u32 set)
63{
Laurent Pinchartcb2025d2013-06-16 21:01:02 +020064 struct rcar_du_device *rcdu = rcrtc->group->dev;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020065 u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
66
67 rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set);
68}
69
Laurent Pinchartf66ee302013-06-14 14:15:01 +020070static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
71{
Laurent Pinchartf66ee302013-06-14 14:15:01 +020072 int ret;
73
74 ret = clk_prepare_enable(rcrtc->clock);
75 if (ret < 0)
76 return ret;
77
Laurent Pinchartcb2025d2013-06-16 21:01:02 +020078 ret = rcar_du_group_get(rcrtc->group);
Laurent Pinchartf66ee302013-06-14 14:15:01 +020079 if (ret < 0)
80 clk_disable_unprepare(rcrtc->clock);
81
82 return ret;
83}
84
85static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
86{
Laurent Pinchartcb2025d2013-06-16 21:01:02 +020087 rcar_du_group_put(rcrtc->group);
Laurent Pinchartf66ee302013-06-14 14:15:01 +020088 clk_disable_unprepare(rcrtc->clock);
89}
90
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020091static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
92{
Laurent Pinchartcb2025d2013-06-16 21:01:02 +020093 const struct drm_display_mode *mode = &rcrtc->crtc.mode;
94 struct rcar_du_device *rcdu = rcrtc->group->dev;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +020095 unsigned long clk;
96 u32 value;
97 u32 div;
98
99 /* Dot clock */
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200100 clk = clk_get_rate(rcrtc->clock);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200101 div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000);
102 div = clamp(div, 1U, 64U) - 1;
103
104 rcar_du_write(rcdu, rcrtc->index ? ESCR2 : ESCR,
105 ESCR_DCLKSEL_CLKS | div);
106 rcar_du_write(rcdu, rcrtc->index ? OTAR2 : OTAR, 0);
107
108 /* Signal polarities */
109 value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
110 | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
111 | DSMR_DIPM_DE;
112 rcar_du_crtc_write(rcrtc, DSMR, value);
113
114 /* Display timings */
115 rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19);
116 rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start +
117 mode->hdisplay - 19);
118 rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end -
119 mode->hsync_start - 1);
120 rcar_du_crtc_write(rcrtc, HCR, mode->htotal - 1);
121
122 rcar_du_crtc_write(rcrtc, VDSR, mode->vtotal - mode->vsync_end - 2);
123 rcar_du_crtc_write(rcrtc, VDER, mode->vtotal - mode->vsync_end +
124 mode->vdisplay - 2);
125 rcar_du_crtc_write(rcrtc, VSPR, mode->vtotal - mode->vsync_end +
126 mode->vsync_start - 1);
127 rcar_du_crtc_write(rcrtc, VCR, mode->vtotal - 1);
128
129 rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start);
130 rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay);
131}
132
133static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc)
134{
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200135 struct rcar_du_device *rcdu = rcrtc->group->dev;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200136 u32 dorcr = rcar_du_read(rcdu, DORCR);
137
138 dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
139
140 /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and
141 * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by
142 * default.
143 */
144 if (rcrtc->outputs & (1 << 1) && rcrtc->index == 0)
145 dorcr |= DORCR_PG2D_DS1;
146 else
147 dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
148
149 rcar_du_write(rcdu, DORCR, dorcr);
150}
151
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200152void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output)
153{
154 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
155
156 /* Store the route from the CRTC output to the DU output. The DU will be
157 * configured when starting the CRTC.
158 */
159 rcrtc->outputs |= 1 << output;
160}
161
162void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
163{
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200164 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200165 struct rcar_du_device *rcdu = rcrtc->group->dev;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200166 struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
167 unsigned int num_planes = 0;
168 unsigned int prio = 0;
169 unsigned int i;
170 u32 dptsr = 0;
171 u32 dspr = 0;
172
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200173 for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) {
174 struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i];
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200175 unsigned int j;
176
177 if (plane->crtc != &rcrtc->crtc || !plane->enabled)
178 continue;
179
180 /* Insert the plane in the sorted planes array. */
181 for (j = num_planes++; j > 0; --j) {
182 if (planes[j-1]->zpos <= plane->zpos)
183 break;
184 planes[j] = planes[j-1];
185 }
186
187 planes[j] = plane;
188 prio += plane->format->planes * 4;
189 }
190
191 for (i = 0; i < num_planes; ++i) {
192 struct rcar_du_plane *plane = planes[i];
193 unsigned int index = plane->hwindex;
194
195 prio -= 4;
196 dspr |= (index + 1) << prio;
197 dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index);
198
199 if (plane->format->planes == 2) {
200 index = (index + 1) % 8;
201
202 prio -= 4;
203 dspr |= (index + 1) << prio;
204 dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index);
205 }
206 }
207
208 /* Select display timing and dot clock generator 2 for planes associated
209 * with superposition controller 2.
210 */
211 if (rcrtc->index) {
212 u32 value = rcar_du_read(rcdu, DPTSR);
213
214 /* The DPTSR register is updated when the display controller is
215 * stopped. We thus need to restart the DU. Once again, sorry
216 * for the flicker. One way to mitigate the issue would be to
217 * pre-associate planes with CRTCs (either with a fixed 4/4
218 * split, or through a module parameter). Flicker would then
219 * occur only if we need to break the pre-association.
220 */
221 if (value != dptsr) {
222 rcar_du_write(rcdu, DPTSR, dptsr);
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200223 if (rcrtc->group->used_crtcs)
224 rcar_du_group_restart(rcrtc->group);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200225 }
226 }
227
228 rcar_du_write(rcdu, rcrtc->index ? DS2PR : DS1PR, dspr);
229}
230
231static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
232{
233 struct drm_crtc *crtc = &rcrtc->crtc;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200234 unsigned int i;
235
236 if (rcrtc->started)
237 return;
238
239 if (WARN_ON(rcrtc->plane->format == NULL))
240 return;
241
242 /* Set display off and background to black */
243 rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
244 rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
245
246 /* Configure display timings and output routing */
247 rcar_du_crtc_set_display_timing(rcrtc);
248 rcar_du_crtc_set_routing(rcrtc);
249
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200250 mutex_lock(&rcrtc->group->planes.lock);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200251 rcrtc->plane->enabled = true;
252 rcar_du_crtc_update_planes(crtc);
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200253 mutex_unlock(&rcrtc->group->planes.lock);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200254
255 /* Setup planes. */
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200256 for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) {
257 struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i];
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200258
259 if (plane->crtc != crtc || !plane->enabled)
260 continue;
261
262 rcar_du_plane_setup(plane);
263 }
264
265 /* Select master sync mode. This enables display operation in master
266 * sync mode (with the HSYNC and VSYNC signals configured as outputs and
267 * actively driven).
268 */
269 rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER);
270
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200271 rcar_du_group_start_stop(rcrtc->group, true);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200272
273 rcrtc->started = true;
274}
275
276static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
277{
278 struct drm_crtc *crtc = &rcrtc->crtc;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200279
280 if (!rcrtc->started)
281 return;
282
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200283 mutex_lock(&rcrtc->group->planes.lock);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200284 rcrtc->plane->enabled = false;
285 rcar_du_crtc_update_planes(crtc);
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200286 mutex_unlock(&rcrtc->group->planes.lock);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200287
288 /* Select switch sync mode. This stops display operation and configures
289 * the HSYNC and VSYNC signals as inputs.
290 */
291 rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH);
292
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200293 rcar_du_group_start_stop(rcrtc->group, false);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200294
295 rcrtc->started = false;
296}
297
298void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc)
299{
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200300 rcar_du_crtc_stop(rcrtc);
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200301 rcar_du_crtc_put(rcrtc);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200302}
303
304void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc)
305{
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200306 if (rcrtc->dpms != DRM_MODE_DPMS_ON)
307 return;
308
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200309 rcar_du_crtc_get(rcrtc);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200310 rcar_du_crtc_start(rcrtc);
311}
312
313static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc)
314{
315 struct drm_crtc *crtc = &rcrtc->crtc;
316
317 rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
318 rcar_du_plane_update_base(rcrtc->plane);
319}
320
321static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode)
322{
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200323 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
324
325 if (rcrtc->dpms == mode)
326 return;
327
328 if (mode == DRM_MODE_DPMS_ON) {
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200329 rcar_du_crtc_get(rcrtc);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200330 rcar_du_crtc_start(rcrtc);
331 } else {
332 rcar_du_crtc_stop(rcrtc);
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200333 rcar_du_crtc_put(rcrtc);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200334 }
335
336 rcrtc->dpms = mode;
337}
338
339static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc,
340 const struct drm_display_mode *mode,
341 struct drm_display_mode *adjusted_mode)
342{
343 /* TODO Fixup modes */
344 return true;
345}
346
347static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc)
348{
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200349 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
350
351 /* We need to access the hardware during mode set, acquire a reference
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200352 * to the CRTC.
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200353 */
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200354 rcar_du_crtc_get(rcrtc);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200355
356 /* Stop the CRTC and release the plane. Force the DPMS mode to off as a
357 * result.
358 */
359 rcar_du_crtc_stop(rcrtc);
360 rcar_du_plane_release(rcrtc->plane);
361
362 rcrtc->dpms = DRM_MODE_DPMS_OFF;
363}
364
365static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
366 struct drm_display_mode *mode,
367 struct drm_display_mode *adjusted_mode,
368 int x, int y,
369 struct drm_framebuffer *old_fb)
370{
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200371 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200372 struct rcar_du_device *rcdu = rcrtc->group->dev;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200373 const struct rcar_du_format_info *format;
374 int ret;
375
376 format = rcar_du_format_info(crtc->fb->pixel_format);
377 if (format == NULL) {
378 dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n",
379 crtc->fb->pixel_format);
380 ret = -EINVAL;
381 goto error;
382 }
383
384 ret = rcar_du_plane_reserve(rcrtc->plane, format);
385 if (ret < 0)
386 goto error;
387
388 rcrtc->plane->format = format;
389 rcrtc->plane->pitch = crtc->fb->pitches[0];
390
391 rcrtc->plane->src_x = x;
392 rcrtc->plane->src_y = y;
393 rcrtc->plane->width = mode->hdisplay;
394 rcrtc->plane->height = mode->vdisplay;
395
396 rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
397
398 rcrtc->outputs = 0;
399
400 return 0;
401
402error:
403 /* There's no rollback/abort operation to clean up in case of error. We
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200404 * thus need to release the reference to the CRTC acquired in prepare()
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200405 * here.
406 */
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200407 rcar_du_crtc_put(rcrtc);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200408 return ret;
409}
410
411static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc)
412{
413 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
414
415 /* We're done, restart the CRTC and set the DPMS mode to on. The
416 * reference to the DU acquired at prepare() time will thus be released
417 * by the DPMS handler (possibly called by the disable() handler).
418 */
419 rcar_du_crtc_start(rcrtc);
420 rcrtc->dpms = DRM_MODE_DPMS_ON;
421}
422
423static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
424 struct drm_framebuffer *old_fb)
425{
426 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
427
428 rcrtc->plane->src_x = x;
429 rcrtc->plane->src_y = y;
430
431 rcar_du_crtc_update_base(to_rcar_crtc(crtc));
432
433 return 0;
434}
435
436static void rcar_du_crtc_disable(struct drm_crtc *crtc)
437{
438 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
439
440 rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
441 rcar_du_plane_release(rcrtc->plane);
442}
443
444static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
445 .dpms = rcar_du_crtc_dpms,
446 .mode_fixup = rcar_du_crtc_mode_fixup,
447 .prepare = rcar_du_crtc_mode_prepare,
448 .commit = rcar_du_crtc_mode_commit,
449 .mode_set = rcar_du_crtc_mode_set,
450 .mode_set_base = rcar_du_crtc_mode_set_base,
451 .disable = rcar_du_crtc_disable,
452};
453
454void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
455 struct drm_file *file)
456{
457 struct drm_pending_vblank_event *event;
458 struct drm_device *dev = rcrtc->crtc.dev;
459 unsigned long flags;
460
461 /* Destroy the pending vertical blanking event associated with the
462 * pending page flip, if any, and disable vertical blanking interrupts.
463 */
464 spin_lock_irqsave(&dev->event_lock, flags);
465 event = rcrtc->event;
466 if (event && event->base.file_priv == file) {
467 rcrtc->event = NULL;
468 event->base.destroy(&event->base);
469 drm_vblank_put(dev, rcrtc->index);
470 }
471 spin_unlock_irqrestore(&dev->event_lock, flags);
472}
473
474static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
475{
476 struct drm_pending_vblank_event *event;
477 struct drm_device *dev = rcrtc->crtc.dev;
478 unsigned long flags;
479
480 spin_lock_irqsave(&dev->event_lock, flags);
481 event = rcrtc->event;
482 rcrtc->event = NULL;
483 spin_unlock_irqrestore(&dev->event_lock, flags);
484
485 if (event == NULL)
486 return;
487
488 spin_lock_irqsave(&dev->event_lock, flags);
489 drm_send_vblank_event(dev, rcrtc->index, event);
490 spin_unlock_irqrestore(&dev->event_lock, flags);
491
492 drm_vblank_put(dev, rcrtc->index);
493}
494
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200495static irqreturn_t rcar_du_crtc_irq(int irq, void *arg)
496{
497 struct rcar_du_crtc *rcrtc = arg;
498 irqreturn_t ret = IRQ_NONE;
499 u32 status;
500
501 status = rcar_du_crtc_read(rcrtc, DSSR);
502 rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK);
503
504 if (status & DSSR_VBK) {
505 drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index);
506 rcar_du_crtc_finish_page_flip(rcrtc);
507 ret = IRQ_HANDLED;
508 }
509
510 return ret;
511}
512
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200513static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
514 struct drm_framebuffer *fb,
515 struct drm_pending_vblank_event *event)
516{
517 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
518 struct drm_device *dev = rcrtc->crtc.dev;
519 unsigned long flags;
520
521 spin_lock_irqsave(&dev->event_lock, flags);
522 if (rcrtc->event != NULL) {
523 spin_unlock_irqrestore(&dev->event_lock, flags);
524 return -EBUSY;
525 }
526 spin_unlock_irqrestore(&dev->event_lock, flags);
527
528 crtc->fb = fb;
529 rcar_du_crtc_update_base(rcrtc);
530
531 if (event) {
532 event->pipe = rcrtc->index;
533 drm_vblank_get(dev, rcrtc->index);
534 spin_lock_irqsave(&dev->event_lock, flags);
535 rcrtc->event = event;
536 spin_unlock_irqrestore(&dev->event_lock, flags);
537 }
538
539 return 0;
540}
541
542static const struct drm_crtc_funcs crtc_funcs = {
543 .destroy = drm_crtc_cleanup,
544 .set_config = drm_crtc_helper_set_config,
545 .page_flip = rcar_du_crtc_page_flip,
546};
547
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200548int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200549{
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200550 struct rcar_du_device *rcdu = rgrp->dev;
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200551 struct platform_device *pdev = to_platform_device(rcdu->dev);
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200552 struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index];
553 struct drm_crtc *crtc = &rcrtc->crtc;
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200554 unsigned int irqflags;
555 char clk_name[5];
556 char *name;
557 int irq;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200558 int ret;
559
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200560 /* Get the CRTC clock. */
561 if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) {
562 sprintf(clk_name, "du.%u", index);
563 name = clk_name;
564 } else {
565 name = NULL;
566 }
567
568 rcrtc->clock = devm_clk_get(rcdu->dev, name);
569 if (IS_ERR(rcrtc->clock)) {
570 dev_err(rcdu->dev, "no clock for CRTC %u\n", index);
571 return PTR_ERR(rcrtc->clock);
572 }
573
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200574 rcrtc->group = rgrp;
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200575 rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0;
576 rcrtc->index = index;
577 rcrtc->dpms = DRM_MODE_DPMS_OFF;
Laurent Pinchartcb2025d2013-06-16 21:01:02 +0200578 rcrtc->plane = &rgrp->planes.planes[index];
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200579
580 rcrtc->plane->crtc = crtc;
581
582 ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs);
583 if (ret < 0)
584 return ret;
585
586 drm_crtc_helper_add(crtc, &crtc_helper_funcs);
587
Laurent Pinchartf66ee302013-06-14 14:15:01 +0200588 /* Register the interrupt handler. */
589 if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) {
590 irq = platform_get_irq(pdev, index);
591 irqflags = 0;
592 } else {
593 irq = platform_get_irq(pdev, 0);
594 irqflags = IRQF_SHARED;
595 }
596
597 if (irq < 0) {
598 dev_err(rcdu->dev, "no IRQ for CRTC %u\n", index);
599 return ret;
600 }
601
602 ret = devm_request_irq(rcdu->dev, irq, rcar_du_crtc_irq, irqflags,
603 dev_name(rcdu->dev), rcrtc);
604 if (ret < 0) {
605 dev_err(rcdu->dev,
606 "failed to register IRQ for CRTC %u\n", index);
607 return ret;
608 }
609
Laurent Pinchart4bf8e192013-06-19 13:54:11 +0200610 return 0;
611}
612
613void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable)
614{
615 if (enable) {
616 rcar_du_crtc_write(rcrtc, DSRCR, DSRCR_VBCL);
617 rcar_du_crtc_set(rcrtc, DIER, DIER_VBE);
618 } else {
619 rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE);
620 }
621}