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