]> rtime.felk.cvut.cz Git - linux-imx.git/commitdiff
Merge branch 'drm/shmob' of git://linuxtv.org/pinchartl/fbdev into drm-next
authorDave Airlie <airlied@redhat.com>
Thu, 27 Jun 2013 08:40:23 +0000 (18:40 +1000)
committerDave Airlie <airlied@redhat.com>
Thu, 27 Jun 2013 08:40:23 +0000 (18:40 +1000)
Fixes for shmob + prime support

* 'drm/shmob' of git://linuxtv.org/pinchartl/fbdev:
  drm/shmobile: Enable compilation on all ARM platforms
  drm/shmobile: Add DRM PRIME support
  drm/shmobile: Use devm_* managed functions
  drm/shmobile: Minor typo fix in debug message

23 files changed:
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_vm.c
drivers/gpu/drm/rcar-du/Kconfig [new file with mode: 0644]
drivers/gpu/drm/rcar-du/Makefile [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_crtc.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_crtc.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_drv.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_drv.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_kms.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_kms.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_lvds.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_lvds.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_plane.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_plane.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_regs.h [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_vga.c [new file with mode: 0644]
drivers/gpu/drm/rcar-du/rcar_du_vga.h [new file with mode: 0644]
drivers/video/console/vgacon.c
include/drm/drm_crtc.h
include/linux/platform_data/rcar-du.h [new file with mode: 0644]

index b16c50ee769c16cfbf87e9a19e0fc2df8c45c327..71ca63b79a4f09f712d295bef8f3d01fe6f1e7a2 100644 (file)
@@ -213,6 +213,8 @@ source "drivers/gpu/drm/mgag200/Kconfig"
 
 source "drivers/gpu/drm/cirrus/Kconfig"
 
+source "drivers/gpu/drm/rcar-du/Kconfig"
+
 source "drivers/gpu/drm/shmobile/Kconfig"
 
 source "drivers/gpu/drm/omapdrm/Kconfig"
index 1ecbe5b7312de6798cd8e76cea4c8e16ae05f140..801bcafa3028b8a04c2b7db7fce10e55c917ea30 100644 (file)
@@ -49,6 +49,7 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
 obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
+obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_OMAP) += omapdrm/
 obj-$(CONFIG_DRM_TILCDC)       += tilcdc/
index c9f9f3ded9e1a6f7772362b1d0e4a518b3fe4d4f..02838e66af76e8578806d956939492fe2b47bf5e 100644 (file)
@@ -1972,18 +1972,31 @@ out:
 int drm_mode_set_config_internal(struct drm_mode_set *set)
 {
        struct drm_crtc *crtc = set->crtc;
-       struct drm_framebuffer *fb, *old_fb;
+       struct drm_framebuffer *fb;
+       struct drm_crtc *tmp;
        int ret;
 
-       old_fb = crtc->fb;
+       /*
+        * NOTE: ->set_config can also disable other crtcs (if we steal all
+        * connectors from it), hence we need to refcount the fbs across all
+        * crtcs. Atomic modeset will have saner semantics ...
+        */
+       list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head)
+               tmp->old_fb = tmp->fb;
+
        fb = set->fb;
 
        ret = crtc->funcs->set_config(set);
        if (ret == 0) {
-               if (old_fb)
-                       drm_framebuffer_unreference(old_fb);
-               if (fb)
-                       drm_framebuffer_reference(fb);
+               /* crtc->fb must be updated by ->set_config, enforces this. */
+               WARN_ON(fb != crtc->fb);
+       }
+
+       list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
+               if (tmp->fb)
+                       drm_framebuffer_reference(tmp->fb);
+               if (tmp->old_fb)
+                       drm_framebuffer_unreference(tmp->old_fb);
        }
 
        return ret;
index f554516ec2a47da91056f7312dd4fcfea3697fa2..738a4294d820e164d7534f830dd4eed7c880aa4d 100644 (file)
@@ -565,14 +565,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
 
        DRM_DEBUG_KMS("\n");
 
-       if (!set)
-               return -EINVAL;
+       BUG_ON(!set);
+       BUG_ON(!set->crtc);
+       BUG_ON(!set->crtc->helper_private);
 
-       if (!set->crtc)
-               return -EINVAL;
-
-       if (!set->crtc->helper_private)
-               return -EINVAL;
+       /* Enforce sane interface api - has been abused by the fb helper. */
+       BUG_ON(!set->mode && set->fb);
+       BUG_ON(set->fb && set->num_connectors == 0);
 
        crtc_funcs = set->crtc->helper_private;
 
@@ -646,11 +645,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                        mode_changed = true;
                } else if (set->fb == NULL) {
                        mode_changed = true;
-               } else if (set->fb->depth != set->crtc->fb->depth) {
-                       mode_changed = true;
-               } else if (set->fb->bits_per_pixel !=
-                          set->crtc->fb->bits_per_pixel) {
-                       mode_changed = true;
                } else if (set->fb->pixel_format !=
                           set->crtc->fb->pixel_format) {
                        mode_changed = true;
@@ -760,12 +754,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                                ret = -EINVAL;
                                goto fail;
                        }
-                       DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
-                       for (i = 0; i < set->num_connectors; i++) {
-                               DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
-                                             drm_get_connector_name(set->connectors[i]));
-                               set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
-                       }
                }
                drm_helper_disable_unused_functions(dev);
        } else if (fb_changed) {
@@ -783,6 +771,22 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                }
        }
 
+       /*
+        * crtc set_config helpers implicit set the crtc and all connected
+        * encoders to DPMS on for a full mode set. But for just an fb update it
+        * doesn't do that. To not confuse userspace, do an explicit DPMS_ON
+        * unconditionally. This will also ensure driver internal dpms state is
+        * consistent again.
+        */
+       if (set->crtc->enabled) {
+               DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
+               for (i = 0; i < set->num_connectors; i++) {
+                       DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
+                                     drm_get_connector_name(set->connectors[i]));
+                       set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
+               }
+       }
+
        kfree(save_connectors);
        kfree(save_encoders);
        kfree(save_crtcs);
index 1575694ccacacfa041656422f0d26e45f458eefd..feb20035b2c44d08ac217c0b48d40967f70f6463 100644 (file)
@@ -613,7 +613,6 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
        case _DRM_FRAME_BUFFER:
        case _DRM_REGISTERS:
                offset = drm_core_get_reg_ofs(dev);
-               vma->vm_flags |= VM_IO; /* not in core dump */
                vma->vm_page_prot = drm_io_prot(map, vma);
                if (io_remap_pfn_range(vma, vma->vm_start,
                                       (map->offset + offset) >> PAGE_SHIFT,
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
new file mode 100644 (file)
index 0000000..72887df
--- /dev/null
@@ -0,0 +1,9 @@
+config DRM_RCAR_DU
+       tristate "DRM Support for R-Car Display Unit"
+       depends on DRM && ARM
+       select DRM_KMS_HELPER
+       select DRM_KMS_CMA_HELPER
+       select DRM_GEM_CMA_HELPER
+       help
+         Choose this option if you have an R-Car chipset.
+         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 (file)
index 0000000..7333c00
--- /dev/null
@@ -0,0 +1,8 @@
+rcar-du-drm-y := rcar_du_crtc.o \
+                rcar_du_drv.o \
+                rcar_du_kms.o \
+                rcar_du_lvds.o \
+                rcar_du_plane.o \
+                rcar_du_vga.o
+
+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 (file)
index 0000000..24183fb
--- /dev/null
@@ -0,0 +1,595 @@
+/*
+ * rcar_du_crtc.c  --  R-Car Display Unit CRTCs
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/mutex.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_lvds.h"
+#include "rcar_du_plane.h"
+#include "rcar_du_regs.h"
+#include "rcar_du_vga.h"
+
+#define to_rcar_crtc(c)        container_of(c, struct rcar_du_crtc, crtc)
+
+static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       return rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
+}
+
+static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data);
+}
+
+static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
+                     rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr);
+}
+
+static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg,
+                     rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set);
+}
+
+static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg,
+                                u32 clr, u32 set)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+       u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
+
+       rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set);
+}
+
+static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_crtc *crtc = &rcrtc->crtc;
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       const struct drm_display_mode *mode = &crtc->mode;
+       unsigned long clk;
+       u32 value;
+       u32 div;
+
+       /* Dot clock */
+       clk = clk_get_rate(rcdu->clock);
+       div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000);
+       div = clamp(div, 1U, 64U) - 1;
+
+       rcar_du_write(rcdu, rcrtc->index ? ESCR2 : ESCR,
+                     ESCR_DCLKSEL_CLKS | div);
+       rcar_du_write(rcdu, rcrtc->index ? OTAR2 : OTAR, 0);
+
+       /* Signal polarities */
+       value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
+             | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
+             | DSMR_DIPM_DE;
+       rcar_du_crtc_write(rcrtc, DSMR, value);
+
+       /* Display timings */
+       rcar_du_crtc_write(rcrtc, HDSR, mode->htotal - mode->hsync_start - 19);
+       rcar_du_crtc_write(rcrtc, HDER, mode->htotal - mode->hsync_start +
+                                       mode->hdisplay - 19);
+       rcar_du_crtc_write(rcrtc, HSWR, mode->hsync_end -
+                                       mode->hsync_start - 1);
+       rcar_du_crtc_write(rcrtc, HCR,  mode->htotal - 1);
+
+       rcar_du_crtc_write(rcrtc, VDSR, mode->vtotal - mode->vsync_end - 2);
+       rcar_du_crtc_write(rcrtc, VDER, mode->vtotal - mode->vsync_end +
+                                       mode->vdisplay - 2);
+       rcar_du_crtc_write(rcrtc, VSPR, mode->vtotal - mode->vsync_end +
+                                       mode->vsync_start - 1);
+       rcar_du_crtc_write(rcrtc, VCR,  mode->vtotal - 1);
+
+       rcar_du_crtc_write(rcrtc, DESR,  mode->htotal - mode->hsync_start);
+       rcar_du_crtc_write(rcrtc, DEWR,  mode->hdisplay);
+}
+
+static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+       u32 dorcr = rcar_du_read(rcdu, DORCR);
+
+       dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
+
+       /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and
+        * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by
+        * default.
+        */
+       if (rcrtc->outputs & (1 << 1) && rcrtc->index == 0)
+               dorcr |= DORCR_PG2D_DS1;
+       else
+               dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
+
+       rcar_du_write(rcdu, DORCR, dorcr);
+}
+
+static void __rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
+{
+       rcar_du_write(rcdu, DSYSR,
+                     (rcar_du_read(rcdu, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) |
+                     (start ? DSYSR_DEN : DSYSR_DRES));
+}
+
+static void rcar_du_start_stop(struct rcar_du_device *rcdu, bool start)
+{
+       /* Many of the configuration bits are only updated when the display
+        * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some
+        * of those bits could be pre-configured, but others (especially the
+        * bits related to plane assignment to display timing controllers) need
+        * to be modified at runtime.
+        *
+        * Restart the display controller if a start is requested. Sorry for the
+        * flicker. It should be possible to move most of the "DRES-update" bits
+        * setup to driver initialization time and minimize the number of cases
+        * when the display controller will have to be restarted.
+        */
+       if (start) {
+               if (rcdu->used_crtcs++ != 0)
+                       __rcar_du_start_stop(rcdu, false);
+               __rcar_du_start_stop(rcdu, true);
+       } else {
+               if (--rcdu->used_crtcs == 0)
+                       __rcar_du_start_stop(rcdu, false);
+       }
+}
+
+void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       /* Store the route from the CRTC output to the DU output. The DU will be
+        * configured when starting the CRTC.
+        */
+       rcrtc->outputs |= 1 << output;
+}
+
+void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
+{
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
+       unsigned int num_planes = 0;
+       unsigned int prio = 0;
+       unsigned int i;
+       u32 dptsr = 0;
+       u32 dspr = 0;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+               struct rcar_du_plane *plane = &rcdu->planes.planes[i];
+               unsigned int j;
+
+               if (plane->crtc != &rcrtc->crtc || !plane->enabled)
+                       continue;
+
+               /* Insert the plane in the sorted planes array. */
+               for (j = num_planes++; j > 0; --j) {
+                       if (planes[j-1]->zpos <= plane->zpos)
+                               break;
+                       planes[j] = planes[j-1];
+               }
+
+               planes[j] = plane;
+               prio += plane->format->planes * 4;
+       }
+
+       for (i = 0; i < num_planes; ++i) {
+               struct rcar_du_plane *plane = planes[i];
+               unsigned int index = plane->hwindex;
+
+               prio -= 4;
+               dspr |= (index + 1) << prio;
+               dptsr |= DPTSR_PnDK(index) |  DPTSR_PnTS(index);
+
+               if (plane->format->planes == 2) {
+                       index = (index + 1) % 8;
+
+                       prio -= 4;
+                       dspr |= (index + 1) << prio;
+                       dptsr |= DPTSR_PnDK(index) |  DPTSR_PnTS(index);
+               }
+       }
+
+       /* Select display timing and dot clock generator 2 for planes associated
+        * with superposition controller 2.
+        */
+       if (rcrtc->index) {
+               u32 value = rcar_du_read(rcdu, DPTSR);
+
+               /* The DPTSR register is updated when the display controller is
+                * stopped. We thus need to restart the DU. Once again, sorry
+                * for the flicker. One way to mitigate the issue would be to
+                * pre-associate planes with CRTCs (either with a fixed 4/4
+                * split, or through a module parameter). Flicker would then
+                * occur only if we need to break the pre-association.
+                */
+               if (value != dptsr) {
+                       rcar_du_write(rcdu, DPTSR, dptsr);
+                       if (rcdu->used_crtcs) {
+                               __rcar_du_start_stop(rcdu, false);
+                               __rcar_du_start_stop(rcdu, true);
+                       }
+               }
+       }
+
+       rcar_du_write(rcdu, rcrtc->index ? DS2PR : DS1PR, dspr);
+}
+
+static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_crtc *crtc = &rcrtc->crtc;
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       unsigned int i;
+
+       if (rcrtc->started)
+               return;
+
+       if (WARN_ON(rcrtc->plane->format == NULL))
+               return;
+
+       /* Set display off and background to black */
+       rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
+       rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
+
+       /* Configure display timings and output routing */
+       rcar_du_crtc_set_display_timing(rcrtc);
+       rcar_du_crtc_set_routing(rcrtc);
+
+       mutex_lock(&rcdu->planes.lock);
+       rcrtc->plane->enabled = true;
+       rcar_du_crtc_update_planes(crtc);
+       mutex_unlock(&rcdu->planes.lock);
+
+       /* Setup planes. */
+       for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+               struct rcar_du_plane *plane = &rcdu->planes.planes[i];
+
+               if (plane->crtc != crtc || !plane->enabled)
+                       continue;
+
+               rcar_du_plane_setup(plane);
+       }
+
+       /* Select master sync mode. This enables display operation in master
+        * sync mode (with the HSYNC and VSYNC signals configured as outputs and
+        * actively driven).
+        */
+       rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER);
+
+       rcar_du_start_stop(rcdu, true);
+
+       rcrtc->started = true;
+}
+
+static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_crtc *crtc = &rcrtc->crtc;
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+
+       if (!rcrtc->started)
+               return;
+
+       mutex_lock(&rcdu->planes.lock);
+       rcrtc->plane->enabled = false;
+       rcar_du_crtc_update_planes(crtc);
+       mutex_unlock(&rcdu->planes.lock);
+
+       /* Select switch sync mode. This stops display operation and configures
+        * the HSYNC and VSYNC signals as inputs.
+        */
+       rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH);
+
+       rcar_du_start_stop(rcdu, false);
+
+       rcrtc->started = false;
+}
+
+void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       rcar_du_crtc_stop(rcrtc);
+       rcar_du_put(rcdu);
+}
+
+void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc)
+{
+       struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private;
+
+       if (rcrtc->dpms != DRM_MODE_DPMS_ON)
+               return;
+
+       rcar_du_get(rcdu);
+       rcar_du_crtc_start(rcrtc);
+}
+
+static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_crtc *crtc = &rcrtc->crtc;
+
+       rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
+       rcar_du_plane_update_base(rcrtc->plane);
+}
+
+static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       if (rcrtc->dpms == mode)
+               return;
+
+       if (mode == DRM_MODE_DPMS_ON) {
+               rcar_du_get(rcdu);
+               rcar_du_crtc_start(rcrtc);
+       } else {
+               rcar_du_crtc_stop(rcrtc);
+               rcar_du_put(rcdu);
+       }
+
+       rcrtc->dpms = mode;
+}
+
+static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc,
+                                   const struct drm_display_mode *mode,
+                                   struct drm_display_mode *adjusted_mode)
+{
+       /* TODO Fixup modes */
+       return true;
+}
+
+static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc)
+{
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       /* We need to access the hardware during mode set, acquire a reference
+        * to the DU.
+        */
+       rcar_du_get(rcdu);
+
+       /* Stop the CRTC and release the plane. Force the DPMS mode to off as a
+        * result.
+        */
+       rcar_du_crtc_stop(rcrtc);
+       rcar_du_plane_release(rcrtc->plane);
+
+       rcrtc->dpms = DRM_MODE_DPMS_OFF;
+}
+
+static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
+                                struct drm_display_mode *mode,
+                                struct drm_display_mode *adjusted_mode,
+                                int x, int y,
+                                struct drm_framebuffer *old_fb)
+{
+       struct rcar_du_device *rcdu = crtc->dev->dev_private;
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       const struct rcar_du_format_info *format;
+       int ret;
+
+       format = rcar_du_format_info(crtc->fb->pixel_format);
+       if (format == NULL) {
+               dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n",
+                       crtc->fb->pixel_format);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       ret = rcar_du_plane_reserve(rcrtc->plane, format);
+       if (ret < 0)
+               goto error;
+
+       rcrtc->plane->format = format;
+       rcrtc->plane->pitch = crtc->fb->pitches[0];
+
+       rcrtc->plane->src_x = x;
+       rcrtc->plane->src_y = y;
+       rcrtc->plane->width = mode->hdisplay;
+       rcrtc->plane->height = mode->vdisplay;
+
+       rcar_du_plane_compute_base(rcrtc->plane, crtc->fb);
+
+       rcrtc->outputs = 0;
+
+       return 0;
+
+error:
+       /* There's no rollback/abort operation to clean up in case of error. We
+        * thus need to release the reference to the DU acquired in prepare()
+        * here.
+        */
+       rcar_du_put(rcdu);
+       return ret;
+}
+
+static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       /* We're done, restart the CRTC and set the DPMS mode to on. The
+        * reference to the DU acquired at prepare() time will thus be released
+        * by the DPMS handler (possibly called by the disable() handler).
+        */
+       rcar_du_crtc_start(rcrtc);
+       rcrtc->dpms = DRM_MODE_DPMS_ON;
+}
+
+static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+                                     struct drm_framebuffer *old_fb)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       rcrtc->plane->src_x = x;
+       rcrtc->plane->src_y = y;
+
+       rcar_du_crtc_update_base(to_rcar_crtc(crtc));
+
+       return 0;
+}
+
+static void rcar_du_crtc_disable(struct drm_crtc *crtc)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+
+       rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       rcar_du_plane_release(rcrtc->plane);
+}
+
+static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+       .dpms = rcar_du_crtc_dpms,
+       .mode_fixup = rcar_du_crtc_mode_fixup,
+       .prepare = rcar_du_crtc_mode_prepare,
+       .commit = rcar_du_crtc_mode_commit,
+       .mode_set = rcar_du_crtc_mode_set,
+       .mode_set_base = rcar_du_crtc_mode_set_base,
+       .disable = rcar_du_crtc_disable,
+};
+
+void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
+                                  struct drm_file *file)
+{
+       struct drm_pending_vblank_event *event;
+       struct drm_device *dev = rcrtc->crtc.dev;
+       unsigned long flags;
+
+       /* Destroy the pending vertical blanking event associated with the
+        * pending page flip, if any, and disable vertical blanking interrupts.
+        */
+       spin_lock_irqsave(&dev->event_lock, flags);
+       event = rcrtc->event;
+       if (event && event->base.file_priv == file) {
+               rcrtc->event = NULL;
+               event->base.destroy(&event->base);
+               drm_vblank_put(dev, rcrtc->index);
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
+{
+       struct drm_pending_vblank_event *event;
+       struct drm_device *dev = rcrtc->crtc.dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       event = rcrtc->event;
+       rcrtc->event = NULL;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       if (event == NULL)
+               return;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       drm_send_vblank_event(dev, rcrtc->index, event);
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       drm_vblank_put(dev, rcrtc->index);
+}
+
+static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
+                                 struct drm_framebuffer *fb,
+                                 struct drm_pending_vblank_event *event)
+{
+       struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct drm_device *dev = rcrtc->crtc.dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (rcrtc->event != NULL) {
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               return -EBUSY;
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       crtc->fb = fb;
+       rcar_du_crtc_update_base(rcrtc);
+
+       if (event) {
+               event->pipe = rcrtc->index;
+               drm_vblank_get(dev, rcrtc->index);
+               spin_lock_irqsave(&dev->event_lock, flags);
+               rcrtc->event = event;
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
+
+       return 0;
+}
+
+static const struct drm_crtc_funcs crtc_funcs = {
+       .destroy = drm_crtc_cleanup,
+       .set_config = drm_crtc_helper_set_config,
+       .page_flip = rcar_du_crtc_page_flip,
+};
+
+int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index)
+{
+       struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index];
+       struct drm_crtc *crtc = &rcrtc->crtc;
+       int ret;
+
+       rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0;
+       rcrtc->index = index;
+       rcrtc->dpms = DRM_MODE_DPMS_OFF;
+       rcrtc->plane = &rcdu->planes.planes[index];
+
+       rcrtc->plane->crtc = crtc;
+
+       ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs);
+       if (ret < 0)
+               return ret;
+
+       drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+
+       return 0;
+}
+
+void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable)
+{
+       if (enable) {
+               rcar_du_crtc_write(rcrtc, DSRCR, DSRCR_VBCL);
+               rcar_du_crtc_set(rcrtc, DIER, DIER_VBE);
+       } else {
+               rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE);
+       }
+}
+
+void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc)
+{
+       u32 status;
+
+       status = rcar_du_crtc_read(rcrtc, DSSR);
+       rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK);
+
+       if (status & DSSR_VBK) {
+               drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index);
+               rcar_du_crtc_finish_page_flip(rcrtc);
+       }
+}
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 (file)
index 0000000..2a0365b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * rcar_du_crtc.h  --  R-Car Display Unit CRTCs
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_CRTC_H__
+#define __RCAR_DU_CRTC_H__
+
+#include <linux/mutex.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+
+struct rcar_du_device;
+struct rcar_du_plane;
+
+struct rcar_du_crtc {
+       struct drm_crtc crtc;
+
+       unsigned int mmio_offset;
+       unsigned int index;
+       bool started;
+
+       struct drm_pending_vblank_event *event;
+       unsigned int outputs;
+       int dpms;
+
+       struct rcar_du_plane *plane;
+};
+
+int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index);
+void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable);
+void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc);
+void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
+                                  struct drm_file *file);
+void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc);
+void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
+
+void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output);
+void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
+
+#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 (file)
index 0000000..003b34e
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * rcar_du_drv.c  --  R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_regs.h"
+
+/* -----------------------------------------------------------------------------
+ * Core device operations
+ */
+
+/*
+ * rcar_du_get - Acquire a reference to the DU
+ *
+ * Acquiring a reference enables the device clock and setup core registers. A
+ * reference must be held before accessing any hardware registers.
+ *
+ * This function must be called with the DRM mode_config lock held.
+ *
+ * Return 0 in case of success or a negative error code otherwise.
+ */
+int rcar_du_get(struct rcar_du_device *rcdu)
+{
+       int ret;
+
+       if (rcdu->use_count)
+               goto done;
+
+       /* Enable clocks before accessing the hardware. */
+       ret = clk_prepare_enable(rcdu->clock);
+       if (ret < 0)
+               return ret;
+
+       /* Enable extended features */
+       rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE);
+       rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G);
+       rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3);
+       rcar_du_write(rcdu, DEFR4, DEFR4_CODE);
+       rcar_du_write(rcdu, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
+
+       /* Use DS1PR and DS2PR to configure planes priorities and connects the
+        * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
+        */
+       rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS);
+
+done:
+       rcdu->use_count++;
+       return 0;
+}
+
+/*
+ * rcar_du_put - Release a reference to the DU
+ *
+ * Releasing the last reference disables the device clock.
+ *
+ * This function must be called with the DRM mode_config lock held.
+ */
+void rcar_du_put(struct rcar_du_device *rcdu)
+{
+       if (--rcdu->use_count)
+               return;
+
+       clk_disable_unprepare(rcdu->clock);
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM operations
+ */
+
+static int rcar_du_unload(struct drm_device *dev)
+{
+       drm_kms_helper_poll_fini(dev);
+       drm_mode_config_cleanup(dev);
+       drm_vblank_cleanup(dev);
+       drm_irq_uninstall(dev);
+
+       dev->dev_private = NULL;
+
+       return 0;
+}
+
+static int rcar_du_load(struct drm_device *dev, unsigned long flags)
+{
+       struct platform_device *pdev = dev->platformdev;
+       struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
+       struct rcar_du_device *rcdu;
+       struct resource *ioarea;
+       struct resource *mem;
+       int ret;
+
+       if (pdata == NULL) {
+               dev_err(dev->dev, "no platform data\n");
+               return -ENODEV;
+       }
+
+       rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
+       if (rcdu == NULL) {
+               dev_err(dev->dev, "failed to allocate private data\n");
+               return -ENOMEM;
+       }
+
+       rcdu->dev = &pdev->dev;
+       rcdu->pdata = pdata;
+       rcdu->ddev = dev;
+       dev->dev_private = rcdu;
+
+       /* I/O resources and clocks */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (mem == NULL) {
+               dev_err(&pdev->dev, "failed to get memory resource\n");
+               return -EINVAL;
+       }
+
+       ioarea = devm_request_mem_region(&pdev->dev, mem->start,
+                                        resource_size(mem), pdev->name);
+       if (ioarea == NULL) {
+               dev_err(&pdev->dev, "failed to request memory region\n");
+               return -EBUSY;
+       }
+
+       rcdu->mmio = devm_ioremap_nocache(&pdev->dev, ioarea->start,
+                                         resource_size(ioarea));
+       if (rcdu->mmio == NULL) {
+               dev_err(&pdev->dev, "failed to remap memory resource\n");
+               return -ENOMEM;
+       }
+
+       rcdu->clock = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(rcdu->clock)) {
+               dev_err(&pdev->dev, "failed to get clock\n");
+               return -ENOENT;
+       }
+
+       /* DRM/KMS objects */
+       ret = rcar_du_modeset_init(rcdu);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
+               goto done;
+       }
+
+       /* IRQ and vblank handling */
+       ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to initialize vblank\n");
+               goto done;
+       }
+
+       ret = drm_irq_install(dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to install IRQ handler\n");
+               goto done;
+       }
+
+       platform_set_drvdata(pdev, rcdu);
+
+done:
+       if (ret)
+               rcar_du_unload(dev);
+
+       return ret;
+}
+
+static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file)
+{
+       struct rcar_du_device *rcdu = dev->dev_private;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
+               rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file);
+}
+
+static irqreturn_t rcar_du_irq(int irq, void *arg)
+{
+       struct drm_device *dev = arg;
+       struct rcar_du_device *rcdu = dev->dev_private;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
+               rcar_du_crtc_irq(&rcdu->crtcs[i]);
+
+       return IRQ_HANDLED;
+}
+
+static int rcar_du_enable_vblank(struct drm_device *dev, int crtc)
+{
+       struct rcar_du_device *rcdu = dev->dev_private;
+
+       rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], true);
+
+       return 0;
+}
+
+static void rcar_du_disable_vblank(struct drm_device *dev, int crtc)
+{
+       struct rcar_du_device *rcdu = dev->dev_private;
+
+       rcar_du_crtc_enable_vblank(&rcdu->crtcs[crtc], false);
+}
+
+static const struct file_operations rcar_du_fops = {
+       .owner          = THIS_MODULE,
+       .open           = drm_open,
+       .release        = drm_release,
+       .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = drm_compat_ioctl,
+#endif
+       .poll           = drm_poll,
+       .read           = drm_read,
+       .fasync         = drm_fasync,
+       .llseek         = no_llseek,
+       .mmap           = drm_gem_cma_mmap,
+};
+
+static struct drm_driver rcar_du_driver = {
+       .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
+                               | DRIVER_PRIME,
+       .load                   = rcar_du_load,
+       .unload                 = rcar_du_unload,
+       .preclose               = rcar_du_preclose,
+       .irq_handler            = rcar_du_irq,
+       .get_vblank_counter     = drm_vblank_count,
+       .enable_vblank          = rcar_du_enable_vblank,
+       .disable_vblank         = rcar_du_disable_vblank,
+       .gem_free_object        = drm_gem_cma_free_object,
+       .gem_vm_ops             = &drm_gem_cma_vm_ops,
+       .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
+       .gem_prime_import       = drm_gem_cma_dmabuf_import,
+       .gem_prime_export       = drm_gem_cma_dmabuf_export,
+       .dumb_create            = drm_gem_cma_dumb_create,
+       .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
+       .dumb_destroy           = drm_gem_cma_dumb_destroy,
+       .fops                   = &rcar_du_fops,
+       .name                   = "rcar-du",
+       .desc                   = "Renesas R-Car Display Unit",
+       .date                   = "20130110",
+       .major                  = 1,
+       .minor                  = 0,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+#if CONFIG_PM_SLEEP
+static int rcar_du_pm_suspend(struct device *dev)
+{
+       struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+
+       drm_kms_helper_poll_disable(rcdu->ddev);
+       /* TODO Suspend the CRTC */
+
+       return 0;
+}
+
+static int rcar_du_pm_resume(struct device *dev)
+{
+       struct rcar_du_device *rcdu = dev_get_drvdata(dev);
+
+       /* TODO Resume the CRTC */
+
+       drm_kms_helper_poll_enable(rcdu->ddev);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops rcar_du_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform driver
+ */
+
+static int rcar_du_probe(struct platform_device *pdev)
+{
+       return drm_platform_init(&rcar_du_driver, pdev);
+}
+
+static int rcar_du_remove(struct platform_device *pdev)
+{
+       drm_platform_exit(&rcar_du_driver, pdev);
+
+       return 0;
+}
+
+static struct platform_driver rcar_du_platform_driver = {
+       .probe          = rcar_du_probe,
+       .remove         = rcar_du_remove,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "rcar-du",
+               .pm     = &rcar_du_pm_ops,
+       },
+};
+
+module_platform_driver(rcar_du_platform_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
+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 (file)
index 0000000..193cc59
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * rcar_du_drv.h  --  R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_DRV_H__
+#define __RCAR_DU_DRV_H__
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/rcar-du.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_plane.h"
+
+struct clk;
+struct device;
+struct drm_device;
+
+struct rcar_du_device {
+       struct device *dev;
+       const struct rcar_du_platform_data *pdata;
+
+       void __iomem *mmio;
+       struct clk *clock;
+       unsigned int use_count;
+
+       struct drm_device *ddev;
+
+       struct rcar_du_crtc crtcs[2];
+       unsigned int used_crtcs;
+       unsigned int num_crtcs;
+
+       struct {
+               struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES];
+               unsigned int free;
+               struct mutex lock;
+
+               struct drm_property *alpha;
+               struct drm_property *colorkey;
+               struct drm_property *zpos;
+       } planes;
+};
+
+int rcar_du_get(struct rcar_du_device *rcdu);
+void rcar_du_put(struct rcar_du_device *rcdu);
+
+static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg)
+{
+       return ioread32(rcdu->mmio + reg);
+}
+
+static inline void rcar_du_write(struct rcar_du_device *rcdu, u32 reg, u32 data)
+{
+       iowrite32(data, rcdu->mmio + reg);
+}
+
+#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 (file)
index 0000000..9c63f39
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * rcar_du_kms.c  --  R-Car Display Unit Mode Setting
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_crtc.h"
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_lvds.h"
+#include "rcar_du_regs.h"
+#include "rcar_du_vga.h"
+
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+static const struct rcar_du_format_info rcar_du_format_infos[] = {
+       {
+               .fourcc = DRM_FORMAT_RGB565,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_ARGB1555,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_XRGB1555,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_ARGB,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_XRGB8888,
+               .bpp = 32,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_TP | PnMR_DDDF_16BPP,
+               .edf = PnDDCR4_EDF_RGB888,
+       }, {
+               .fourcc = DRM_FORMAT_ARGB8888,
+               .bpp = 32,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_ALP | PnMR_DDDF_16BPP,
+               .edf = PnDDCR4_EDF_ARGB8888,
+       }, {
+               .fourcc = DRM_FORMAT_UYVY,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_YUYV,
+               .bpp = 16,
+               .planes = 1,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_NV12,
+               .bpp = 12,
+               .planes = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               .fourcc = DRM_FORMAT_NV21,
+               .bpp = 12,
+               .planes = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       }, {
+               /* In YUV 4:2:2, only NV16 is supported (NV61 isn't) */
+               .fourcc = DRM_FORMAT_NV16,
+               .bpp = 16,
+               .planes = 2,
+               .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC,
+               .edf = PnDDCR4_EDF_NONE,
+       },
+};
+
+const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(rcar_du_format_infos); ++i) {
+               if (rcar_du_format_infos[i].fourcc == fourcc)
+                       return &rcar_du_format_infos[i];
+       }
+
+       return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Common connector and encoder functions
+ */
+
+struct drm_encoder *
+rcar_du_connector_best_encoder(struct drm_connector *connector)
+{
+       struct rcar_du_connector *rcon = to_rcar_connector(connector);
+
+       return &rcon->encoder->encoder;
+}
+
+void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder)
+{
+}
+
+void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
+                             struct drm_display_mode *mode,
+                             struct drm_display_mode *adjusted_mode)
+{
+       struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+
+       rcar_du_crtc_route_output(encoder->crtc, renc->output);
+}
+
+void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
+{
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer
+ */
+
+static struct drm_framebuffer *
+rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+                 struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       const struct rcar_du_format_info *format;
+
+       format = rcar_du_format_info(mode_cmd->pixel_format);
+       if (format == NULL) {
+               dev_dbg(dev->dev, "unsupported pixel format %08x\n",
+                       mode_cmd->pixel_format);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (mode_cmd->pitches[0] & 15 || mode_cmd->pitches[0] >= 8192) {
+               dev_dbg(dev->dev, "invalid pitch value %u\n",
+                       mode_cmd->pitches[0]);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (format->planes == 2) {
+               if (mode_cmd->pitches[1] != mode_cmd->pitches[0]) {
+                       dev_dbg(dev->dev,
+                               "luma and chroma pitches do not match\n");
+                       return ERR_PTR(-EINVAL);
+               }
+       }
+
+       return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+
+static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
+       .fb_create = rcar_du_fb_create,
+};
+
+int rcar_du_modeset_init(struct rcar_du_device *rcdu)
+{
+       struct drm_device *dev = rcdu->ddev;
+       struct drm_encoder *encoder;
+       unsigned int i;
+       int ret;
+
+       drm_mode_config_init(rcdu->ddev);
+
+       rcdu->ddev->mode_config.min_width = 0;
+       rcdu->ddev->mode_config.min_height = 0;
+       rcdu->ddev->mode_config.max_width = 4095;
+       rcdu->ddev->mode_config.max_height = 2047;
+       rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs;
+
+       ret = rcar_du_plane_init(rcdu);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i)
+               rcar_du_crtc_create(rcdu, i);
+
+       rcdu->used_crtcs = 0;
+       rcdu->num_crtcs = i;
+
+       for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
+               const struct rcar_du_encoder_data *pdata =
+                       &rcdu->pdata->encoders[i];
+
+               if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) {
+                       dev_warn(rcdu->dev,
+                                "encoder %u references unexisting output %u, skipping\n",
+                                i, pdata->output);
+                       continue;
+               }
+
+               switch (pdata->encoder) {
+               case RCAR_DU_ENCODER_VGA:
+                       rcar_du_vga_init(rcdu, &pdata->u.vga, pdata->output);
+                       break;
+
+               case RCAR_DU_ENCODER_LVDS:
+                       rcar_du_lvds_init(rcdu, &pdata->u.lvds, pdata->output);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       /* Set the possible CRTCs and possible clones. All encoders can be
+        * driven by the CRTC associated with the output they're connected to,
+        * as well as by CRTC 0.
+        */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+
+               encoder->possible_crtcs = (1 << 0) | (1 << renc->output);
+               encoder->possible_clones = 1 << 0;
+       }
+
+       ret = rcar_du_plane_register(rcdu);
+       if (ret < 0)
+               return ret;
+
+       drm_kms_helper_poll_init(rcdu->ddev);
+
+       drm_helper_disable_unused_functions(rcdu->ddev);
+
+       return 0;
+}
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 (file)
index 0000000..e4d8db0
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * rcar_du_kms.h  --  R-Car Display Unit Mode Setting
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_KMS_H__
+#define __RCAR_DU_KMS_H__
+
+#include <linux/types.h>
+
+#include <drm/drm_crtc.h>
+
+struct rcar_du_device;
+
+struct rcar_du_format_info {
+       u32 fourcc;
+       unsigned int bpp;
+       unsigned int planes;
+       unsigned int pnmr;
+       unsigned int edf;
+};
+
+struct rcar_du_encoder {
+       struct drm_encoder encoder;
+       unsigned int output;
+};
+
+#define to_rcar_encoder(e) \
+       container_of(e, struct rcar_du_encoder, encoder)
+
+struct rcar_du_connector {
+       struct drm_connector connector;
+       struct rcar_du_encoder *encoder;
+};
+
+#define to_rcar_connector(c) \
+       container_of(c, struct rcar_du_connector, connector)
+
+const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc);
+
+struct drm_encoder *
+rcar_du_connector_best_encoder(struct drm_connector *connector);
+void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder);
+void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
+                             struct drm_display_mode *mode,
+                             struct drm_display_mode *adjusted_mode);
+void rcar_du_encoder_mode_commit(struct drm_encoder *encoder);
+
+int rcar_du_modeset_init(struct rcar_du_device *rcdu);
+
+#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 (file)
index 0000000..7aefe72
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * rcar_du_lvds.c  --  R-Car Display Unit LVDS Encoder and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_lvds.h"
+
+struct rcar_du_lvds_connector {
+       struct rcar_du_connector connector;
+
+       const struct rcar_du_panel_data *panel;
+};
+
+#define to_rcar_lvds_connector(c) \
+       container_of(c, struct rcar_du_lvds_connector, connector.connector)
+
+/* -----------------------------------------------------------------------------
+ * Connector
+ */
+
+static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
+{
+       struct rcar_du_lvds_connector *lvdscon = to_rcar_lvds_connector(connector);
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_create(connector->dev);
+       if (mode == NULL)
+               return 0;
+
+       mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
+       mode->clock = lvdscon->panel->mode.clock;
+       mode->hdisplay = lvdscon->panel->mode.hdisplay;
+       mode->hsync_start = lvdscon->panel->mode.hsync_start;
+       mode->hsync_end = lvdscon->panel->mode.hsync_end;
+       mode->htotal = lvdscon->panel->mode.htotal;
+       mode->vdisplay = lvdscon->panel->mode.vdisplay;
+       mode->vsync_start = lvdscon->panel->mode.vsync_start;
+       mode->vsync_end = lvdscon->panel->mode.vsync_end;
+       mode->vtotal = lvdscon->panel->mode.vtotal;
+       mode->flags = lvdscon->panel->mode.flags;
+
+       drm_mode_set_name(mode);
+       drm_mode_probed_add(connector, mode);
+
+       return 1;
+}
+
+static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector,
+                                           struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+       .get_modes = rcar_du_lvds_connector_get_modes,
+       .mode_valid = rcar_du_lvds_connector_mode_valid,
+       .best_encoder = rcar_du_connector_best_encoder,
+};
+
+static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
+{
+       drm_sysfs_connector_remove(connector);
+       drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
+{
+       return connector_status_connected;
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .detect = rcar_du_lvds_connector_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = rcar_du_lvds_connector_destroy,
+};
+
+static int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
+                                      struct rcar_du_encoder *renc,
+                                      const struct rcar_du_panel_data *panel)
+{
+       struct rcar_du_lvds_connector *lvdscon;
+       struct drm_connector *connector;
+       int ret;
+
+       lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL);
+       if (lvdscon == NULL)
+               return -ENOMEM;
+
+       lvdscon->panel = panel;
+
+       connector = &lvdscon->connector.connector;
+       connector->display_info.width_mm = panel->width_mm;
+       connector->display_info.height_mm = panel->height_mm;
+
+       ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
+                                DRM_MODE_CONNECTOR_LVDS);
+       if (ret < 0)
+               return ret;
+
+       drm_connector_helper_add(connector, &connector_helper_funcs);
+       ret = drm_sysfs_connector_add(connector);
+       if (ret < 0)
+               return ret;
+
+       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+       drm_object_property_set_value(&connector->base,
+               rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+       ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
+       if (ret < 0)
+               return ret;
+
+       connector->encoder = &renc->encoder;
+       lvdscon->connector.encoder = renc;
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Encoder
+ */
+
+static void rcar_du_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool rcar_du_lvds_encoder_mode_fixup(struct drm_encoder *encoder,
+                                          const struct drm_display_mode *mode,
+                                          struct drm_display_mode *adjusted_mode)
+{
+       const struct drm_display_mode *panel_mode;
+       struct drm_device *dev = encoder->dev;
+       struct drm_connector *connector;
+       bool found = false;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (connector->encoder == encoder) {
+                       found = true;
+                       break;
+               }
+       }
+
+       if (!found) {
+               dev_dbg(dev->dev, "mode_fixup: no connector found\n");
+               return false;
+       }
+
+       if (list_empty(&connector->modes)) {
+               dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
+               return false;
+       }
+
+       panel_mode = list_first_entry(&connector->modes,
+                                     struct drm_display_mode, head);
+
+       /* We're not allowed to modify the resolution. */
+       if (mode->hdisplay != panel_mode->hdisplay ||
+           mode->vdisplay != panel_mode->vdisplay)
+               return false;
+
+       /* The flat panel mode is fixed, just copy it to the adjusted mode. */
+       drm_mode_copy(adjusted_mode, panel_mode);
+
+       return true;
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+       .dpms = rcar_du_lvds_encoder_dpms,
+       .mode_fixup = rcar_du_lvds_encoder_mode_fixup,
+       .prepare = rcar_du_encoder_mode_prepare,
+       .commit = rcar_du_encoder_mode_commit,
+       .mode_set = rcar_du_encoder_mode_set,
+};
+
+static const struct drm_encoder_funcs encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+int rcar_du_lvds_init(struct rcar_du_device *rcdu,
+                     const struct rcar_du_encoder_lvds_data *data,
+                     unsigned int output)
+{
+       struct rcar_du_encoder *renc;
+       int ret;
+
+       renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
+       if (renc == NULL)
+               return -ENOMEM;
+
+       renc->output = output;
+
+       ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
+                              DRM_MODE_ENCODER_LVDS);
+       if (ret < 0)
+               return ret;
+
+       drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
+
+       return rcar_du_lvds_connector_init(rcdu, renc, &data->panel);
+}
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 (file)
index 0000000..b47f832
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * rcar_du_lvds.h  --  R-Car Display Unit LVDS Encoder and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_LVDS_H__
+#define __RCAR_DU_LVDS_H__
+
+struct rcar_du_device;
+struct rcar_du_encoder_lvds_data;
+
+int rcar_du_lvds_init(struct rcar_du_device *rcdu,
+                     const struct rcar_du_encoder_lvds_data *data,
+                     unsigned int output);
+
+#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 (file)
index 0000000..a65f81d
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * rcar_du_plane.c  --  R-Car Display Unit Planes
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_plane.h"
+#include "rcar_du_regs.h"
+
+#define RCAR_DU_COLORKEY_NONE          (0 << 24)
+#define RCAR_DU_COLORKEY_SOURCE                (1 << 24)
+#define RCAR_DU_COLORKEY_MASK          (1 << 24)
+
+struct rcar_du_kms_plane {
+       struct drm_plane plane;
+       struct rcar_du_plane *hwplane;
+};
+
+static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
+{
+       return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane;
+}
+
+static u32 rcar_du_plane_read(struct rcar_du_device *rcdu,
+                             unsigned int index, u32 reg)
+{
+       return rcar_du_read(rcdu, index * PLANE_OFF + reg);
+}
+
+static void rcar_du_plane_write(struct rcar_du_device *rcdu,
+                               unsigned int index, u32 reg, u32 data)
+{
+       rcar_du_write(rcdu, index * PLANE_OFF + reg, data);
+}
+
+int rcar_du_plane_reserve(struct rcar_du_plane *plane,
+                         const struct rcar_du_format_info *format)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+       unsigned int i;
+       int ret = -EBUSY;
+
+       mutex_lock(&rcdu->planes.lock);
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+               if (!(rcdu->planes.free & (1 << i)))
+                       continue;
+
+               if (format->planes == 1 ||
+                   rcdu->planes.free & (1 << ((i + 1) % 8)))
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(rcdu->planes.planes))
+               goto done;
+
+       rcdu->planes.free &= ~(1 << i);
+       if (format->planes == 2)
+               rcdu->planes.free &= ~(1 << ((i + 1) % 8));
+
+       plane->hwindex = i;
+
+       ret = 0;
+
+done:
+       mutex_unlock(&rcdu->planes.lock);
+       return ret;
+}
+
+void rcar_du_plane_release(struct rcar_du_plane *plane)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+
+       if (plane->hwindex == -1)
+               return;
+
+       mutex_lock(&rcdu->planes.lock);
+       rcdu->planes.free |= 1 << plane->hwindex;
+       if (plane->format->planes == 2)
+               rcdu->planes.free |= 1 << ((plane->hwindex + 1) % 8);
+       mutex_unlock(&rcdu->planes.lock);
+
+       plane->hwindex = -1;
+}
+
+void rcar_du_plane_update_base(struct rcar_du_plane *plane)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+       unsigned int index = plane->hwindex;
+
+       /* According to the datasheet the Y position is expressed in raster line
+        * units. However, 32bpp formats seem to require a doubled Y position
+        * value. Similarly, for the second plane, NV12 and NV21 formats seem to
+        * require a halved Y position value.
+        */
+       rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
+       rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y *
+                           (plane->format->bpp == 32 ? 2 : 1));
+       rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[0]);
+
+       if (plane->format->planes == 2) {
+               index = (index + 1) % 8;
+
+               rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x);
+               rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y *
+                                   (plane->format->bpp == 16 ? 2 : 1) / 2);
+               rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[1]);
+       }
+}
+
+void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
+                               struct drm_framebuffer *fb)
+{
+       struct drm_gem_cma_object *gem;
+
+       gem = drm_fb_cma_get_gem_obj(fb, 0);
+       plane->dma[0] = gem->paddr + fb->offsets[0];
+
+       if (plane->format->planes == 2) {
+               gem = drm_fb_cma_get_gem_obj(fb, 1);
+               plane->dma[1] = gem->paddr + fb->offsets[1];
+       }
+}
+
+static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
+                                    unsigned int index)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+       u32 colorkey;
+       u32 pnmr;
+
+       /* The PnALPHAR register controls alpha-blending in 16bpp formats
+        * (ARGB1555 and XRGB1555).
+        *
+        * For ARGB, set the alpha value to 0, and enable alpha-blending when
+        * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255.
+        *
+        * For XRGB, set the alpha value to the plane-wide alpha value and
+        * enable alpha-blending regardless of the X bit value.
+        */
+       if (plane->format->fourcc != DRM_FORMAT_XRGB1555)
+               rcar_du_plane_write(rcdu, index, PnALPHAR, PnALPHAR_ABIT_0);
+       else
+               rcar_du_plane_write(rcdu, index, PnALPHAR,
+                                   PnALPHAR_ABIT_X | plane->alpha);
+
+       pnmr = PnMR_BM_MD | plane->format->pnmr;
+
+       /* Disable color keying when requested. YUV formats have the
+        * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
+        * automatically.
+        */
+       if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
+               pnmr |= PnMR_SPIM_TP_OFF;
+
+       /* For packed YUV formats we need to select the U/V order. */
+       if (plane->format->fourcc == DRM_FORMAT_YUYV)
+               pnmr |= PnMR_YCDF_YUYV;
+
+       rcar_du_plane_write(rcdu, index, PnMR, pnmr);
+
+       switch (plane->format->fourcc) {
+       case DRM_FORMAT_RGB565:
+               colorkey = ((plane->colorkey & 0xf80000) >> 8)
+                        | ((plane->colorkey & 0x00fc00) >> 5)
+                        | ((plane->colorkey & 0x0000f8) >> 3);
+               rcar_du_plane_write(rcdu, index, PnTC2R, colorkey);
+               break;
+
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_XRGB1555:
+               colorkey = ((plane->colorkey & 0xf80000) >> 9)
+                        | ((plane->colorkey & 0x00f800) >> 6)
+                        | ((plane->colorkey & 0x0000f8) >> 3);
+               rcar_du_plane_write(rcdu, index, PnTC2R, colorkey);
+               break;
+
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_ARGB8888:
+               rcar_du_plane_write(rcdu, index, PnTC3R,
+                                   PnTC3R_CODE | (plane->colorkey & 0xffffff));
+               break;
+       }
+}
+
+static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
+                                 unsigned int index)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+       u32 ddcr2 = PnDDCR2_CODE;
+       u32 ddcr4;
+       u32 mwr;
+
+       /* Data format
+        *
+        * The data format is selected by the DDDF field in PnMR and the EDF
+        * field in DDCR4.
+        */
+       ddcr4 = rcar_du_plane_read(rcdu, index, PnDDCR4);
+       ddcr4 &= ~PnDDCR4_EDF_MASK;
+       ddcr4 |= plane->format->edf | PnDDCR4_CODE;
+
+       rcar_du_plane_setup_mode(plane, index);
+
+       if (plane->format->planes == 2) {
+               if (plane->hwindex != index) {
+                       if (plane->format->fourcc == DRM_FORMAT_NV12 ||
+                           plane->format->fourcc == DRM_FORMAT_NV21)
+                               ddcr2 |= PnDDCR2_Y420;
+
+                       if (plane->format->fourcc == DRM_FORMAT_NV21)
+                               ddcr2 |= PnDDCR2_NV21;
+
+                       ddcr2 |= PnDDCR2_DIVU;
+               } else {
+                       ddcr2 |= PnDDCR2_DIVY;
+               }
+       }
+
+       rcar_du_plane_write(rcdu, index, PnDDCR2, ddcr2);
+       rcar_du_plane_write(rcdu, index, PnDDCR4, ddcr4);
+
+       /* Memory pitch (expressed in pixels) */
+       if (plane->format->planes == 2)
+               mwr = plane->pitch;
+       else
+               mwr = plane->pitch * 8 / plane->format->bpp;
+
+       rcar_du_plane_write(rcdu, index, PnMWR, mwr);
+
+       /* Destination position and size */
+       rcar_du_plane_write(rcdu, index, PnDSXR, plane->width);
+       rcar_du_plane_write(rcdu, index, PnDSYR, plane->height);
+       rcar_du_plane_write(rcdu, index, PnDPXR, plane->dst_x);
+       rcar_du_plane_write(rcdu, index, PnDPYR, plane->dst_y);
+
+       /* Wrap-around and blinking, disabled */
+       rcar_du_plane_write(rcdu, index, PnWASPR, 0);
+       rcar_du_plane_write(rcdu, index, PnWAMWR, 4095);
+       rcar_du_plane_write(rcdu, index, PnBTR, 0);
+       rcar_du_plane_write(rcdu, index, PnMLR, 0);
+}
+
+void rcar_du_plane_setup(struct rcar_du_plane *plane)
+{
+       __rcar_du_plane_setup(plane, plane->hwindex);
+       if (plane->format->planes == 2)
+               __rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8);
+
+       rcar_du_plane_update_base(plane);
+}
+
+static int
+rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
+                      struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+                      unsigned int crtc_w, unsigned int crtc_h,
+                      uint32_t src_x, uint32_t src_y,
+                      uint32_t src_w, uint32_t src_h)
+{
+       struct rcar_du_plane *rplane = to_rcar_plane(plane);
+       struct rcar_du_device *rcdu = plane->dev->dev_private;
+       const struct rcar_du_format_info *format;
+       unsigned int nplanes;
+       int ret;
+
+       format = rcar_du_format_info(fb->pixel_format);
+       if (format == NULL) {
+               dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
+                       fb->pixel_format);
+               return -EINVAL;
+       }
+
+       if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
+               dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
+               return -EINVAL;
+       }
+
+       nplanes = rplane->format ? rplane->format->planes : 0;
+
+       /* Reallocate hardware planes if the number of required planes has
+        * changed.
+        */
+       if (format->planes != nplanes) {
+               rcar_du_plane_release(rplane);
+               ret = rcar_du_plane_reserve(rplane, format);
+               if (ret < 0)
+                       return ret;
+       }
+
+       rplane->crtc = crtc;
+       rplane->format = format;
+       rplane->pitch = fb->pitches[0];
+
+       rplane->src_x = src_x >> 16;
+       rplane->src_y = src_y >> 16;
+       rplane->dst_x = crtc_x;
+       rplane->dst_y = crtc_y;
+       rplane->width = crtc_w;
+       rplane->height = crtc_h;
+
+       rcar_du_plane_compute_base(rplane, fb);
+       rcar_du_plane_setup(rplane);
+
+       mutex_lock(&rcdu->planes.lock);
+       rplane->enabled = true;
+       rcar_du_crtc_update_planes(rplane->crtc);
+       mutex_unlock(&rcdu->planes.lock);
+
+       return 0;
+}
+
+static int rcar_du_plane_disable(struct drm_plane *plane)
+{
+       struct rcar_du_device *rcdu = plane->dev->dev_private;
+       struct rcar_du_plane *rplane = to_rcar_plane(plane);
+
+       if (!rplane->enabled)
+               return 0;
+
+       mutex_lock(&rcdu->planes.lock);
+       rplane->enabled = false;
+       rcar_du_crtc_update_planes(rplane->crtc);
+       mutex_unlock(&rcdu->planes.lock);
+
+       rcar_du_plane_release(rplane);
+
+       rplane->crtc = NULL;
+       rplane->format = NULL;
+
+       return 0;
+}
+
+/* Both the .set_property and the .update_plane operations are called with the
+ * mode_config lock held. There is this no need to explicitly protect access to
+ * the alpha and colorkey fields and the mode register.
+ */
+static void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha)
+{
+       if (plane->alpha == alpha)
+               return;
+
+       plane->alpha = alpha;
+       if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555)
+               return;
+
+       rcar_du_plane_setup_mode(plane, plane->hwindex);
+}
+
+static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane,
+                                      u32 colorkey)
+{
+       if (plane->colorkey == colorkey)
+               return;
+
+       plane->colorkey = colorkey;
+       if (!plane->enabled)
+               return;
+
+       rcar_du_plane_setup_mode(plane, plane->hwindex);
+}
+
+static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane,
+                                  unsigned int zpos)
+{
+       struct rcar_du_device *rcdu = plane->dev;
+
+       mutex_lock(&rcdu->planes.lock);
+       if (plane->zpos == zpos)
+               goto done;
+
+       plane->zpos = zpos;
+       if (!plane->enabled)
+               goto done;
+
+       rcar_du_crtc_update_planes(plane->crtc);
+
+done:
+       mutex_unlock(&rcdu->planes.lock);
+}
+
+static int rcar_du_plane_set_property(struct drm_plane *plane,
+                                     struct drm_property *property,
+                                     uint64_t value)
+{
+       struct rcar_du_device *rcdu = plane->dev->dev_private;
+       struct rcar_du_plane *rplane = to_rcar_plane(plane);
+
+       if (property == rcdu->planes.alpha)
+               rcar_du_plane_set_alpha(rplane, value);
+       else if (property == rcdu->planes.colorkey)
+               rcar_du_plane_set_colorkey(rplane, value);
+       else if (property == rcdu->planes.zpos)
+               rcar_du_plane_set_zpos(rplane, value);
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static const struct drm_plane_funcs rcar_du_plane_funcs = {
+       .update_plane = rcar_du_plane_update,
+       .disable_plane = rcar_du_plane_disable,
+       .set_property = rcar_du_plane_set_property,
+       .destroy = drm_plane_cleanup,
+};
+
+static const uint32_t formats[] = {
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_NV12,
+       DRM_FORMAT_NV21,
+       DRM_FORMAT_NV16,
+};
+
+int rcar_du_plane_init(struct rcar_du_device *rcdu)
+{
+       unsigned int i;
+
+       mutex_init(&rcdu->planes.lock);
+       rcdu->planes.free = 0xff;
+
+       rcdu->planes.alpha =
+               drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
+       if (rcdu->planes.alpha == NULL)
+               return -ENOMEM;
+
+       /* The color key is expressed as an RGB888 triplet stored in a 32-bit
+        * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
+        * or enable source color keying (1).
+        */
+       rcdu->planes.colorkey =
+               drm_property_create_range(rcdu->ddev, 0, "colorkey",
+                                         0, 0x01ffffff);
+       if (rcdu->planes.colorkey == NULL)
+               return -ENOMEM;
+
+       rcdu->planes.zpos =
+               drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7);
+       if (rcdu->planes.zpos == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) {
+               struct rcar_du_plane *plane = &rcdu->planes.planes[i];
+
+               plane->dev = rcdu;
+               plane->hwindex = -1;
+               plane->alpha = 255;
+               plane->colorkey = RCAR_DU_COLORKEY_NONE;
+               plane->zpos = 0;
+       }
+
+       return 0;
+}
+
+int rcar_du_plane_register(struct rcar_du_device *rcdu)
+{
+       unsigned int i;
+       int ret;
+
+       for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) {
+               struct rcar_du_kms_plane *plane;
+
+               plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL);
+               if (plane == NULL)
+                       return -ENOMEM;
+
+               plane->hwplane = &rcdu->planes.planes[i + 2];
+               plane->hwplane->zpos = 1;
+
+               ret = drm_plane_init(rcdu->ddev, &plane->plane,
+                                    (1 << rcdu->num_crtcs) - 1,
+                                    &rcar_du_plane_funcs, formats,
+                                    ARRAY_SIZE(formats), false);
+               if (ret < 0)
+                       return ret;
+
+               drm_object_attach_property(&plane->plane.base,
+                                          rcdu->planes.alpha, 255);
+               drm_object_attach_property(&plane->plane.base,
+                                          rcdu->planes.colorkey,
+                                          RCAR_DU_COLORKEY_NONE);
+               drm_object_attach_property(&plane->plane.base,
+                                          rcdu->planes.zpos, 1);
+       }
+
+       return 0;
+}
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 (file)
index 0000000..5397dba
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * rcar_du_plane.h  --  R-Car Display Unit Planes
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_PLANE_H__
+#define __RCAR_DU_PLANE_H__
+
+struct drm_crtc;
+struct drm_framebuffer;
+struct rcar_du_device;
+struct rcar_du_format_info;
+
+/* The RCAR DU has 8 hardware planes, shared between KMS planes and CRTCs. As
+ * using KMS planes requires at least one of the CRTCs being enabled, no more
+ * than 7 KMS planes can be available. We thus create 7 KMS planes and
+ * 9 software planes (one for each KMS planes and one for each CRTC).
+ */
+
+#define RCAR_DU_NUM_KMS_PLANES         7
+#define RCAR_DU_NUM_HW_PLANES          8
+#define RCAR_DU_NUM_SW_PLANES          9
+
+struct rcar_du_plane {
+       struct rcar_du_device *dev;
+       struct drm_crtc *crtc;
+
+       bool enabled;
+
+       int hwindex;            /* 0-based, -1 means unused */
+       unsigned int alpha;
+       unsigned int colorkey;
+       unsigned int zpos;
+
+       const struct rcar_du_format_info *format;
+
+       unsigned long dma[2];
+       unsigned int pitch;
+
+       unsigned int width;
+       unsigned int height;
+
+       unsigned int src_x;
+       unsigned int src_y;
+       unsigned int dst_x;
+       unsigned int dst_y;
+};
+
+int rcar_du_plane_init(struct rcar_du_device *rcdu);
+int rcar_du_plane_register(struct rcar_du_device *rcdu);
+void rcar_du_plane_setup(struct rcar_du_plane *plane);
+void rcar_du_plane_update_base(struct rcar_du_plane *plane);
+void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
+                               struct drm_framebuffer *fb);
+int rcar_du_plane_reserve(struct rcar_du_plane *plane,
+                         const struct rcar_du_format_info *format);
+void rcar_du_plane_release(struct rcar_du_plane *plane);
+
+#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 (file)
index 0000000..69f21f1
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ * rcar_du_regs.h  --  R-Car Display Unit Registers Definitions
+ *
+ * Copyright (C) 2013 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef __RCAR_DU_REGS_H__
+#define __RCAR_DU_REGS_H__
+
+#define DISP2_REG_OFFSET        0x30000
+
+/* -----------------------------------------------------------------------------
+ * Display Control Registers
+ */
+
+#define DSYSR                  0x00000 /* display 1 */
+#define D2SYSR                 0x30000 /* display 2 */
+#define DSYSR_ILTS             (1 << 29)
+#define DSYSR_DSEC             (1 << 20)
+#define DSYSR_IUPD             (1 << 16)
+#define DSYSR_DRES             (1 << 9)
+#define DSYSR_DEN              (1 << 8)
+#define DSYSR_TVM_MASTER       (0 << 6)
+#define DSYSR_TVM_SWITCH       (1 << 6)
+#define DSYSR_TVM_TVSYNC       (2 << 6)
+#define DSYSR_TVM_MASK         (3 << 6)
+#define DSYSR_SCM_INT_NONE     (0 << 4)
+#define DSYSR_SCM_INT_SYNC     (2 << 4)
+#define DSYSR_SCM_INT_VIDEO    (3 << 4)
+
+#define DSMR                   0x00004
+#define D2SMR                  0x30004
+#define DSMR_VSPM              (1 << 28)
+#define DSMR_ODPM              (1 << 27)
+#define DSMR_DIPM_DISP         (0 << 25)
+#define DSMR_DIPM_CSYNC                (1 << 25)
+#define DSMR_DIPM_DE           (3 << 25)
+#define DSMR_DIPM_MASK         (3 << 25)
+#define DSMR_CSPM              (1 << 24)
+#define DSMR_DIL               (1 << 19)
+#define DSMR_VSL               (1 << 18)
+#define DSMR_HSL               (1 << 17)
+#define DSMR_DDIS              (1 << 16)
+#define DSMR_CDEL              (1 << 15)
+#define DSMR_CDEM_CDE          (0 << 13)
+#define DSMR_CDEM_LOW          (2 << 13)
+#define DSMR_CDEM_HIGH         (3 << 13)
+#define DSMR_CDEM_MASK         (3 << 13)
+#define DSMR_CDED              (1 << 12)
+#define DSMR_ODEV              (1 << 8)
+#define DSMR_CSY_VH_OR         (0 << 6)
+#define DSMR_CSY_333           (2 << 6)
+#define DSMR_CSY_222           (3 << 6)
+#define DSMR_CSY_MASK          (3 << 6)
+
+#define DSSR                   0x00008
+#define D2SSR                  0x30008
+#define DSSR_VC1FB_DSA0                (0 << 30)
+#define DSSR_VC1FB_DSA1                (1 << 30)
+#define DSSR_VC1FB_DSA2                (2 << 30)
+#define DSSR_VC1FB_INIT                (3 << 30)
+#define DSSR_VC1FB_MASK                (3 << 30)
+#define DSSR_VC0FB_DSA0                (0 << 28)
+#define DSSR_VC0FB_DSA1                (1 << 28)
+#define DSSR_VC0FB_DSA2                (2 << 28)
+#define DSSR_VC0FB_INIT                (3 << 28)
+#define DSSR_VC0FB_MASK                (3 << 28)
+#define DSSR_DFB(n)            (1 << ((n)+15))
+#define DSSR_TVR               (1 << 15)
+#define DSSR_FRM               (1 << 14)
+#define DSSR_VBK               (1 << 11)
+#define DSSR_RINT              (1 << 9)
+#define DSSR_HBK               (1 << 8)
+#define DSSR_ADC(n)            (1 << ((n)-1))
+
+#define DSRCR                  0x0000c
+#define D2SRCR                 0x3000c
+#define DSRCR_TVCL             (1 << 15)
+#define DSRCR_FRCL             (1 << 14)
+#define DSRCR_VBCL             (1 << 11)
+#define DSRCR_RICL             (1 << 9)
+#define DSRCR_HBCL             (1 << 8)
+#define DSRCR_ADCL(n)          (1 << ((n)-1))
+#define DSRCR_MASK             0x0000cbff
+
+#define DIER                   0x00010
+#define D2IER                  0x30010
+#define DIER_TVE               (1 << 15)
+#define DIER_FRE               (1 << 14)
+#define DIER_VBE               (1 << 11)
+#define DIER_RIE               (1 << 9)
+#define DIER_HBE               (1 << 8)
+#define DIER_ADCE(n)           (1 << ((n)-1))
+
+#define CPCR                   0x00014
+#define CPCR_CP4CE             (1 << 19)
+#define CPCR_CP3CE             (1 << 18)
+#define CPCR_CP2CE             (1 << 17)
+#define CPCR_CP1CE             (1 << 16)
+
+#define DPPR                   0x00018
+#define DPPR_DPE(n)            (1 << ((n)*4-1))
+#define DPPR_DPS(n, p)         (((p)-1) << DPPR_DPS_SHIFT(n))
+#define DPPR_DPS_SHIFT(n)      (((n)-1)*4)
+#define DPPR_BPP16             (DPPR_DPE(8) | DPPR_DPS(8, 1))  /* plane1 */
+#define DPPR_BPP32_P1          (DPPR_DPE(7) | DPPR_DPS(7, 1))
+#define DPPR_BPP32_P2          (DPPR_DPE(8) | DPPR_DPS(8, 2))
+#define DPPR_BPP32             (DPPR_BPP32_P1 | DPPR_BPP32_P2) /* plane1 & 2 */
+
+#define DEFR                   0x00020
+#define D2EFR                  0x30020
+#define DEFR_CODE              (0x7773 << 16)
+#define DEFR_EXSL              (1 << 12)
+#define DEFR_EXVL              (1 << 11)
+#define DEFR_EXUP              (1 << 5)
+#define DEFR_VCUP              (1 << 4)
+#define DEFR_DEFE              (1 << 0)
+
+#define DAPCR                  0x00024
+#define DAPCR_CODE             (0x7773 << 16)
+#define DAPCR_AP2E             (1 << 4)
+#define DAPCR_AP1E             (1 << 0)
+
+#define DCPCR                  0x00028
+#define DCPCR_CODE             (0x7773 << 16)
+#define DCPCR_CA2B             (1 << 13)
+#define DCPCR_CD2F             (1 << 12)
+#define DCPCR_DC2E             (1 << 8)
+#define DCPCR_CAB              (1 << 5)
+#define DCPCR_CDF              (1 << 4)
+#define DCPCR_DCE              (1 << 0)
+
+#define DEFR2                  0x00034
+#define D2EFR2                 0x30034
+#define DEFR2_CODE             (0x7775 << 16)
+#define DEFR2_DEFE2G           (1 << 0)
+
+#define DEFR3                  0x00038
+#define D2EFR3                 0x30038
+#define DEFR3_CODE             (0x7776 << 16)
+#define DEFR3_EVDA             (1 << 14)
+#define DEFR3_EVDM_1           (1 << 12)
+#define DEFR3_EVDM_2           (2 << 12)
+#define DEFR3_EVDM_3           (3 << 12)
+#define DEFR3_VMSM2_EMA                (1 << 6)
+#define DEFR3_VMSM1_ENA                (1 << 4)
+#define DEFR3_DEFE3            (1 << 0)
+
+#define DEFR4                  0x0003c
+#define D2EFR4                 0x3003c
+#define DEFR4_CODE             (0x7777 << 16)
+#define DEFR4_LRUO             (1 << 5)
+#define DEFR4_SPCE             (1 << 4)
+
+#define DVCSR                  0x000d0
+#define DVCSR_VCnFB2_DSA0(n)   (0 << ((n)*2+16))
+#define DVCSR_VCnFB2_DSA1(n)   (1 << ((n)*2+16))
+#define DVCSR_VCnFB2_DSA2(n)   (2 << ((n)*2+16))
+#define DVCSR_VCnFB2_INIT(n)   (3 << ((n)*2+16))
+#define DVCSR_VCnFB2_MASK(n)   (3 << ((n)*2+16))
+#define DVCSR_VCnFB_DSA0(n)    (0 << ((n)*2))
+#define DVCSR_VCnFB_DSA1(n)    (1 << ((n)*2))
+#define DVCSR_VCnFB_DSA2(n)    (2 << ((n)*2))
+#define DVCSR_VCnFB_INIT(n)    (3 << ((n)*2))
+#define DVCSR_VCnFB_MASK(n)    (3 << ((n)*2))
+
+#define DEFR5                  0x000e0
+#define DEFR5_CODE             (0x66 << 24)
+#define DEFR5_YCRGB2_DIS       (0 << 14)
+#define DEFR5_YCRGB2_PRI1      (1 << 14)
+#define DEFR5_YCRGB2_PRI2      (2 << 14)
+#define DEFR5_YCRGB2_PRI3      (3 << 14)
+#define DEFR5_YCRGB2_MASK      (3 << 14)
+#define DEFR5_YCRGB1_DIS       (0 << 12)
+#define DEFR5_YCRGB1_PRI1      (1 << 12)
+#define DEFR5_YCRGB1_PRI2      (2 << 12)
+#define DEFR5_YCRGB1_PRI3      (3 << 12)
+#define DEFR5_YCRGB1_MASK      (3 << 12)
+#define DEFR5_DEFE5            (1 << 0)
+
+#define DDLTR                  0x000e4
+#define DDLTR_CODE             (0x7766 << 16)
+#define DDLTR_DLAR2            (1 << 6)
+#define DDLTR_DLAY2            (1 << 5)
+#define DDLTR_DLAY1            (1 << 1)
+
+#define DEFR6                  0x000e8
+#define DEFR6_CODE             (0x7778 << 16)
+#define DEFR6_ODPM22_D2SMR     (0 << 10)
+#define DEFR6_ODPM22_DISP      (2 << 10)
+#define DEFR6_ODPM22_CDE       (3 << 10)
+#define DEFR6_ODPM22_MASK      (3 << 10)
+#define DEFR6_ODPM12_DSMR      (0 << 8)
+#define DEFR6_ODPM12_DISP      (2 << 8)
+#define DEFR6_ODPM12_CDE       (3 << 8)
+#define DEFR6_ODPM12_MASK      (3 << 8)
+#define DEFR6_TCNE2            (1 << 6)
+#define DEFR6_MLOS1            (1 << 2)
+#define DEFR6_DEFAULT          (DEFR6_CODE | DEFR6_TCNE2)
+
+/* -----------------------------------------------------------------------------
+ * Display Timing Generation Registers
+ */
+
+#define HDSR                   0x00040
+#define HDER                   0x00044
+#define VDSR                   0x00048
+#define VDER                   0x0004c
+#define HCR                    0x00050
+#define HSWR                   0x00054
+#define VCR                    0x00058
+#define VSPR                   0x0005c
+#define EQWR                   0x00060
+#define SPWR                   0x00064
+#define CLAMPSR                        0x00070
+#define CLAMPWR                        0x00074
+#define DESR                   0x00078
+#define DEWR                   0x0007c
+
+/* -----------------------------------------------------------------------------
+ * Display Attribute Registers
+ */
+
+#define CP1TR                  0x00080
+#define CP2TR                  0x00084
+#define CP3TR                  0x00088
+#define CP4TR                  0x0008c
+
+#define DOOR                   0x00090
+#define DOOR_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
+#define CDER                   0x00094
+#define CDER_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
+#define BPOR                   0x00098
+#define BPOR_RGB(r, g, b)      (((r) << 18) | ((g) << 10) | ((b) << 2))
+
+#define RINTOFSR               0x0009c
+
+#define DSHPR                  0x000c8
+#define DSHPR_CODE             (0x7776 << 16)
+#define DSHPR_PRIH             (0xa << 4)
+#define DSHPR_PRIL_BPP16       (0x8 << 0)
+#define DSHPR_PRIL_BPP32       (0x9 << 0)
+
+/* -----------------------------------------------------------------------------
+ * Display Plane Registers
+ */
+
+#define PLANE_OFF              0x00100
+
+#define PnMR                   0x00100 /* plane 1 */
+#define PnMR_VISL_VIN0         (0 << 26)       /* use Video Input 0 */
+#define PnMR_VISL_VIN1         (1 << 26)       /* use Video Input 1 */
+#define PnMR_VISL_VIN2         (2 << 26)       /* use Video Input 2 */
+#define PnMR_VISL_VIN3         (3 << 26)       /* use Video Input 3 */
+#define PnMR_YCDF_YUYV         (1 << 20)       /* YUYV format */
+#define PnMR_TC_R              (0 << 17)       /* Tranparent color is PnTC1R */
+#define PnMR_TC_CP             (1 << 17)       /* Tranparent color is color palette */
+#define PnMR_WAE               (1 << 16)       /* Wrap around Enable */
+#define PnMR_SPIM_TP           (0 << 12)       /* Transparent Color */
+#define PnMR_SPIM_ALP          (1 << 12)       /* Alpha Blending */
+#define PnMR_SPIM_EOR          (2 << 12)       /* EOR */
+#define PnMR_SPIM_TP_OFF       (1 << 14)       /* No Transparent Color */
+#define PnMR_CPSL_CP1          (0 << 8)        /* Color Palette selected 1 */
+#define PnMR_CPSL_CP2          (1 << 8)        /* Color Palette selected 2 */
+#define PnMR_CPSL_CP3          (2 << 8)        /* Color Palette selected 3 */
+#define PnMR_CPSL_CP4          (3 << 8)        /* Color Palette selected 4 */
+#define PnMR_DC                        (1 << 7)        /* Display Area Change */
+#define PnMR_BM_MD             (0 << 4)        /* Manual Display Change Mode */
+#define PnMR_BM_AR             (1 << 4)        /* Auto Rendering Mode */
+#define PnMR_BM_AD             (2 << 4)        /* Auto Display Change Mode */
+#define PnMR_BM_VC             (3 << 4)        /* Video Capture Mode */
+#define PnMR_DDDF_8BPP         (0 << 0)        /* 8bit */
+#define PnMR_DDDF_16BPP                (1 << 0)        /* 16bit or 32bit */
+#define PnMR_DDDF_ARGB         (2 << 0)        /* ARGB */
+#define PnMR_DDDF_YC           (3 << 0)        /* YC */
+#define PnMR_DDDF_MASK         (3 << 0)
+
+#define PnMWR                  0x00104
+
+#define PnALPHAR               0x00108
+#define PnALPHAR_ABIT_1                (0 << 12)
+#define PnALPHAR_ABIT_0                (1 << 12)
+#define PnALPHAR_ABIT_X                (2 << 12)
+
+#define PnDSXR                 0x00110
+#define PnDSYR                 0x00114
+#define PnDPXR                 0x00118
+#define PnDPYR                 0x0011c
+
+#define PnDSA0R                        0x00120
+#define PnDSA1R                        0x00124
+#define PnDSA2R                        0x00128
+#define PnDSA_MASK             0xfffffff0
+
+#define PnSPXR                 0x00130
+#define PnSPYR                 0x00134
+#define PnWASPR                        0x00138
+#define PnWAMWR                        0x0013c
+
+#define PnBTR                  0x00140
+
+#define PnTC1R                 0x00144
+#define PnTC2R                 0x00148
+#define PnTC3R                 0x0014c
+#define PnTC3R_CODE            (0x66 << 24)
+
+#define PnMLR                  0x00150
+
+#define PnSWAPR                        0x00180
+#define PnSWAPR_DIGN           (1 << 4)
+#define PnSWAPR_SPQW           (1 << 3)
+#define PnSWAPR_SPLW           (1 << 2)
+#define PnSWAPR_SPWD           (1 << 1)
+#define PnSWAPR_SPBY           (1 << 0)
+
+#define PnDDCR                 0x00184
+#define PnDDCR_CODE            (0x7775 << 16)
+#define PnDDCR_LRGB1           (1 << 11)
+#define PnDDCR_LRGB0           (1 << 10)
+
+#define PnDDCR2                        0x00188
+#define PnDDCR2_CODE           (0x7776 << 16)
+#define PnDDCR2_NV21           (1 << 5)
+#define PnDDCR2_Y420           (1 << 4)
+#define PnDDCR2_DIVU           (1 << 1)
+#define PnDDCR2_DIVY           (1 << 0)
+
+#define PnDDCR4                        0x00190
+#define PnDDCR4_CODE           (0x7766 << 16)
+#define PnDDCR4_SDFS_RGB       (0 << 4)
+#define PnDDCR4_SDFS_YC                (5 << 4)
+#define PnDDCR4_SDFS_MASK      (7 << 4)
+#define PnDDCR4_EDF_NONE       (0 << 0)
+#define PnDDCR4_EDF_ARGB8888   (1 << 0)
+#define PnDDCR4_EDF_RGB888     (2 << 0)
+#define PnDDCR4_EDF_RGB666     (3 << 0)
+#define PnDDCR4_EDF_MASK       (7 << 0)
+
+#define APnMR                  0x0a100
+#define APnMR_WAE              (1 << 16)       /* Wrap around Enable */
+#define APnMR_DC               (1 << 7)        /* Display Area Change */
+#define APnMR_BM_MD            (0 << 4)        /* Manual Display Change Mode */
+#define APnMR_BM_AD            (2 << 4)        /* Auto Display Change Mode */
+
+#define APnMWR                 0x0a104
+#define APnDSA0R               0x0a120
+#define APnDSA1R               0x0a124
+#define APnDSA2R               0x0a128
+#define APnMLR                 0x0a150
+
+/* -----------------------------------------------------------------------------
+ * Display Capture Registers
+ */
+
+#define DCMWR                  0x0c104
+#define DC2MWR                 0x0c204
+#define DCSAR                  0x0c120
+#define DC2SAR                 0x0c220
+#define DCMLR                  0x0c150
+#define DC2MLR                 0x0c250
+
+/* -----------------------------------------------------------------------------
+ * Color Palette Registers
+ */
+
+#define CP1_000R               0x01000
+#define CP1_255R               0x013fc
+#define CP2_000R               0x02000
+#define CP2_255R               0x023fc
+#define CP3_000R               0x03000
+#define CP3_255R               0x033fc
+#define CP4_000R               0x04000
+#define CP4_255R               0x043fc
+
+/* -----------------------------------------------------------------------------
+ * External Synchronization Control Registers
+ */
+
+#define ESCR                   0x10000
+#define ESCR2                  0x31000
+#define ESCR_DCLKOINV          (1 << 25)
+#define ESCR_DCLKSEL_DCLKIN    (0 << 20)
+#define ESCR_DCLKSEL_CLKS      (1 << 20)
+#define ESCR_DCLKSEL_MASK      (1 << 20)
+#define ESCR_DCLKDIS           (1 << 16)
+#define ESCR_SYNCSEL_OFF       (0 << 8)
+#define ESCR_SYNCSEL_EXVSYNC   (2 << 8)
+#define ESCR_SYNCSEL_EXHSYNC   (3 << 8)
+#define ESCR_FRQSEL_MASK       (0x3f << 0)
+
+#define OTAR                   0x10004
+#define OTAR2                  0x31004
+
+/* -----------------------------------------------------------------------------
+ * Dual Display Output Control Registers
+ */
+
+#define DORCR                  0x11000
+#define DORCR_PG2T             (1 << 30)
+#define DORCR_DK2S             (1 << 28)
+#define DORCR_PG2D_DS1         (0 << 24)
+#define DORCR_PG2D_DS2         (1 << 24)
+#define DORCR_PG2D_FIX0                (2 << 24)
+#define DORCR_PG2D_DOOR                (3 << 24)
+#define DORCR_PG2D_MASK                (3 << 24)
+#define DORCR_DR1D             (1 << 21)
+#define DORCR_PG1D_DS1         (0 << 16)
+#define DORCR_PG1D_DS2         (1 << 16)
+#define DORCR_PG1D_FIX0                (2 << 16)
+#define DORCR_PG1D_DOOR                (3 << 16)
+#define DORCR_PG1D_MASK                (3 << 16)
+#define DORCR_RGPV             (1 << 4)
+#define DORCR_DPRS             (1 << 0)
+
+#define DPTSR                  0x11004
+#define DPTSR_PnDK(n)          (1 << ((n) + 16))
+#define DPTSR_PnTS(n)          (1 << (n))
+
+#define DAPTSR                 0x11008
+#define DAPTSR_APnDK(n)                (1 << ((n) + 16))
+#define DAPTSR_APnTS(n)                (1 << (n))
+
+#define DS1PR                  0x11020
+#define DS2PR                  0x11024
+
+/* -----------------------------------------------------------------------------
+ * YC-RGB Conversion Coefficient Registers
+ */
+
+#define YNCR                   0x11080
+#define YNOR                   0x11084
+#define CRNOR                  0x11088
+#define CBNOR                  0x1108c
+#define RCRCR                  0x11090
+#define GCRCR                  0x11094
+#define GCBCR                  0x11098
+#define BCBCR                  0x1109c
+
+#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 (file)
index 0000000..327289e
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * rcar_du_vga.c  --  R-Car Display Unit VGA DAC and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_kms.h"
+#include "rcar_du_vga.h"
+
+/* -----------------------------------------------------------------------------
+ * Connector
+ */
+
+static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
+{
+       return 0;
+}
+
+static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector,
+                                           struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+       .get_modes = rcar_du_vga_connector_get_modes,
+       .mode_valid = rcar_du_vga_connector_mode_valid,
+       .best_encoder = rcar_du_connector_best_encoder,
+};
+
+static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
+{
+       drm_sysfs_connector_remove(connector);
+       drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
+{
+       return connector_status_unknown;
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .detect = rcar_du_vga_connector_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = rcar_du_vga_connector_destroy,
+};
+
+static int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
+                                     struct rcar_du_encoder *renc)
+{
+       struct rcar_du_connector *rcon;
+       struct drm_connector *connector;
+       int ret;
+
+       rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
+       if (rcon == NULL)
+               return -ENOMEM;
+
+       connector = &rcon->connector;
+       connector->display_info.width_mm = 0;
+       connector->display_info.height_mm = 0;
+
+       ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
+                                DRM_MODE_CONNECTOR_VGA);
+       if (ret < 0)
+               return ret;
+
+       drm_connector_helper_add(connector, &connector_helper_funcs);
+       ret = drm_sysfs_connector_add(connector);
+       if (ret < 0)
+               return ret;
+
+       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+       drm_object_property_set_value(&connector->base,
+               rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+       ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
+       if (ret < 0)
+               return ret;
+
+       connector->encoder = &renc->encoder;
+       rcon->encoder = renc;
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Encoder
+ */
+
+static void rcar_du_vga_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool rcar_du_vga_encoder_mode_fixup(struct drm_encoder *encoder,
+                                          const struct drm_display_mode *mode,
+                                          struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
+       .dpms = rcar_du_vga_encoder_dpms,
+       .mode_fixup = rcar_du_vga_encoder_mode_fixup,
+       .prepare = rcar_du_encoder_mode_prepare,
+       .commit = rcar_du_encoder_mode_commit,
+       .mode_set = rcar_du_encoder_mode_set,
+};
+
+static const struct drm_encoder_funcs encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+int rcar_du_vga_init(struct rcar_du_device *rcdu,
+                    const struct rcar_du_encoder_vga_data *data,
+                    unsigned int output)
+{
+       struct rcar_du_encoder *renc;
+       int ret;
+
+       renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
+       if (renc == NULL)
+               return -ENOMEM;
+
+       renc->output = output;
+
+       ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
+                              DRM_MODE_ENCODER_DAC);
+       if (ret < 0)
+               return ret;
+
+       drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
+
+       return rcar_du_vga_connector_init(rcdu, renc);
+}
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 (file)
index 0000000..66b4d2d
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * rcar_du_vga.h  --  R-Car Display Unit VGA DAC and Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_VGA_H__
+#define __RCAR_DU_VGA_H__
+
+struct rcar_du_device;
+struct rcar_du_encoder_vga_data;
+
+int rcar_du_vga_init(struct rcar_du_device *rcdu,
+                    const struct rcar_du_encoder_vga_data *data,
+                    unsigned int output);
+
+#endif /* __RCAR_DU_VGA_H__ */
index 5855d17d19ac9b8d493c13d7f0dd879ccf0710e4..9d8feac676373fc63df9d3300736f71d710a641d 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/kd.h>
 #include <linux/slab.h>
 #include <linux/vt_kern.h>
+#include <linux/sched.h>
 #include <linux/selection.h>
 #include <linux/spinlock.h>
 #include <linux/ioport.h>
@@ -1124,11 +1125,15 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
 
        if (arg) {
                if (set)
-                       for (i = 0; i < cmapsz; i++)
+                       for (i = 0; i < cmapsz; i++) {
                                vga_writeb(arg[i], charmap + i);
+                               cond_resched();
+                       }
                else
-                       for (i = 0; i < cmapsz; i++)
+                       for (i = 0; i < cmapsz; i++) {
                                arg[i] = vga_readb(charmap + i);
+                               cond_resched();
+                       }
 
                /*
                 * In 512-character mode, the character map is not contiguous if
@@ -1139,11 +1144,15 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512)
                        charmap += 2 * cmapsz;
                        arg += cmapsz;
                        if (set)
-                               for (i = 0; i < cmapsz; i++)
+                               for (i = 0; i < cmapsz; i++) {
                                        vga_writeb(arg[i], charmap + i);
+                                       cond_resched();
+                               }
                        else
-                               for (i = 0; i < cmapsz; i++)
+                               for (i = 0; i < cmapsz; i++) {
                                        arg[i] = vga_readb(charmap + i);
+                                       cond_resched();
+                               }
                }
        }
 
index 9779ea11e63d63c470feb5e8a97348f6044328ba..b9ddd3d42acf30dae74fb6818f45dca0d7bbd0c5 100644 (file)
@@ -409,6 +409,10 @@ struct drm_crtc {
        /* framebuffer the connector is currently bound to */
        struct drm_framebuffer *fb;
 
+       /* Temporary tracking of the old fb while a modeset is ongoing. Used
+        * by drm_mode_set_config_internal to implement correct refcounting. */
+       struct drm_framebuffer *old_fb;
+
        bool enabled;
 
        /* Requested mode from modesetting. */
diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h
new file mode 100644 (file)
index 0000000..80587fd
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * rcar_du.h  --  R-Car Display Unit DRM driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_H__
+#define __RCAR_DU_H__
+
+#include <drm/drm_mode.h>
+
+enum rcar_du_encoder_type {
+       RCAR_DU_ENCODER_UNUSED = 0,
+       RCAR_DU_ENCODER_VGA,
+       RCAR_DU_ENCODER_LVDS,
+};
+
+struct rcar_du_panel_data {
+       unsigned int width_mm;          /* Panel width in mm */
+       unsigned int height_mm;         /* Panel height in mm */
+       struct drm_mode_modeinfo mode;
+};
+
+struct rcar_du_encoder_lvds_data {
+       struct rcar_du_panel_data panel;
+};
+
+struct rcar_du_encoder_vga_data {
+       /* TODO: Add DDC information for EDID retrieval */
+};
+
+struct rcar_du_encoder_data {
+       enum rcar_du_encoder_type encoder;
+       unsigned int output;
+
+       union {
+               struct rcar_du_encoder_lvds_data lvds;
+               struct rcar_du_encoder_vga_data vga;
+       } u;
+};
+
+struct rcar_du_platform_data {
+       struct rcar_du_encoder_data *encoders;
+       unsigned int num_encoders;
+};
+
+#endif /* __RCAR_DU_H__ */