1/* 2 * rcar_du_plane.c -- R-Car Display Unit Planes 3 * 4 * Copyright (C) 2013-2014 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 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_atomic_helper.h> 16#include <drm/drm_crtc.h> 17#include <drm/drm_crtc_helper.h> 18#include <drm/drm_fb_cma_helper.h> 19#include <drm/drm_gem_cma_helper.h> 20#include <drm/drm_plane_helper.h> 21 22#include "rcar_du_drv.h" 23#include "rcar_du_kms.h" 24#include "rcar_du_plane.h" 25#include "rcar_du_regs.h" 26 27#define RCAR_DU_COLORKEY_NONE (0 << 24) 28#define RCAR_DU_COLORKEY_SOURCE (1 << 24) 29#define RCAR_DU_COLORKEY_MASK (1 << 24) 30 31static u32 rcar_du_plane_read(struct rcar_du_group *rgrp, 32 unsigned int index, u32 reg) 33{ 34 return rcar_du_read(rgrp->dev, 35 rgrp->mmio_offset + index * PLANE_OFF + reg); 36} 37 38static void rcar_du_plane_write(struct rcar_du_group *rgrp, 39 unsigned int index, u32 reg, u32 data) 40{ 41 rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg, 42 data); 43} 44 45static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane) 46{ 47 struct rcar_du_plane_state *state = 48 to_rcar_du_plane_state(plane->plane.state); 49 struct drm_framebuffer *fb = plane->plane.state->fb; 50 struct rcar_du_group *rgrp = plane->group; 51 unsigned int src_x = state->state.src_x >> 16; 52 unsigned int src_y = state->state.src_y >> 16; 53 unsigned int index = state->hwindex; 54 struct drm_gem_cma_object *gem; 55 bool interlaced; 56 u32 mwr; 57 58 interlaced = state->state.crtc->state->adjusted_mode.flags 59 & DRM_MODE_FLAG_INTERLACE; 60 61 /* Memory pitch (expressed in pixels). Must be doubled for interlaced 62 * operation with 32bpp formats. 63 */ 64 if (state->format->planes == 2) 65 mwr = fb->pitches[0]; 66 else 67 mwr = fb->pitches[0] * 8 / state->format->bpp; 68 69 if (interlaced && state->format->bpp == 32) 70 mwr *= 2; 71 72 rcar_du_plane_write(rgrp, index, PnMWR, mwr); 73 74 /* The Y position is expressed in raster line units and must be doubled 75 * for 32bpp formats, according to the R8A7790 datasheet. No mention of 76 * doubling the Y position is found in the R8A7779 datasheet, but the 77 * rule seems to apply there as well. 78 * 79 * Despite not being documented, doubling seem not to be needed when 80 * operating in interlaced mode. 81 * 82 * Similarly, for the second plane, NV12 and NV21 formats seem to 83 * require a halved Y position value, in both progressive and interlaced 84 * modes. 85 */ 86 rcar_du_plane_write(rgrp, index, PnSPXR, src_x); 87 rcar_du_plane_write(rgrp, index, PnSPYR, src_y * 88 (!interlaced && state->format->bpp == 32 ? 2 : 1)); 89 90 gem = drm_fb_cma_get_gem_obj(fb, 0); 91 rcar_du_plane_write(rgrp, index, PnDSA0R, gem->paddr + fb->offsets[0]); 92 93 if (state->format->planes == 2) { 94 index = (index + 1) % 8; 95 96 rcar_du_plane_write(rgrp, index, PnMWR, fb->pitches[0]); 97 98 rcar_du_plane_write(rgrp, index, PnSPXR, src_x); 99 rcar_du_plane_write(rgrp, index, PnSPYR, src_y * 100 (state->format->bpp == 16 ? 2 : 1) / 2); 101 102 gem = drm_fb_cma_get_gem_obj(fb, 1); 103 rcar_du_plane_write(rgrp, index, PnDSA0R, 104 gem->paddr + fb->offsets[1]); 105 } 106} 107 108static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, 109 unsigned int index) 110{ 111 struct rcar_du_plane_state *state = 112 to_rcar_du_plane_state(plane->plane.state); 113 struct rcar_du_group *rgrp = plane->group; 114 u32 colorkey; 115 u32 pnmr; 116 117 /* The PnALPHAR register controls alpha-blending in 16bpp formats 118 * (ARGB1555 and XRGB1555). 119 * 120 * For ARGB, set the alpha value to 0, and enable alpha-blending when 121 * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255. 122 * 123 * For XRGB, set the alpha value to the plane-wide alpha value and 124 * enable alpha-blending regardless of the X bit value. 125 */ 126 if (state->format->fourcc != DRM_FORMAT_XRGB1555) 127 rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0); 128 else 129 rcar_du_plane_write(rgrp, index, PnALPHAR, 130 PnALPHAR_ABIT_X | state->alpha); 131 132 pnmr = PnMR_BM_MD | state->format->pnmr; 133 134 /* Disable color keying when requested. YUV formats have the 135 * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying 136 * automatically. 137 */ 138 if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE) 139 pnmr |= PnMR_SPIM_TP_OFF; 140 141 /* For packed YUV formats we need to select the U/V order. */ 142 if (state->format->fourcc == DRM_FORMAT_YUYV) 143 pnmr |= PnMR_YCDF_YUYV; 144 145 rcar_du_plane_write(rgrp, index, PnMR, pnmr); 146 147 switch (state->format->fourcc) { 148 case DRM_FORMAT_RGB565: 149 colorkey = ((state->colorkey & 0xf80000) >> 8) 150 | ((state->colorkey & 0x00fc00) >> 5) 151 | ((state->colorkey & 0x0000f8) >> 3); 152 rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); 153 break; 154 155 case DRM_FORMAT_ARGB1555: 156 case DRM_FORMAT_XRGB1555: 157 colorkey = ((state->colorkey & 0xf80000) >> 9) 158 | ((state->colorkey & 0x00f800) >> 6) 159 | ((state->colorkey & 0x0000f8) >> 3); 160 rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); 161 break; 162 163 case DRM_FORMAT_XRGB8888: 164 case DRM_FORMAT_ARGB8888: 165 rcar_du_plane_write(rgrp, index, PnTC3R, 166 PnTC3R_CODE | (state->colorkey & 0xffffff)); 167 break; 168 } 169} 170 171static void __rcar_du_plane_setup(struct rcar_du_plane *plane, 172 unsigned int index) 173{ 174 struct rcar_du_plane_state *state = 175 to_rcar_du_plane_state(plane->plane.state); 176 struct rcar_du_group *rgrp = plane->group; 177 u32 ddcr2 = PnDDCR2_CODE; 178 u32 ddcr4; 179 180 /* Data format 181 * 182 * The data format is selected by the DDDF field in PnMR and the EDF 183 * field in DDCR4. 184 */ 185 ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4); 186 ddcr4 &= ~PnDDCR4_EDF_MASK; 187 ddcr4 |= state->format->edf | PnDDCR4_CODE; 188 189 rcar_du_plane_setup_mode(plane, index); 190 191 if (state->format->planes == 2) { 192 if (state->hwindex != index) { 193 if (state->format->fourcc == DRM_FORMAT_NV12 || 194 state->format->fourcc == DRM_FORMAT_NV21) 195 ddcr2 |= PnDDCR2_Y420; 196 197 if (state->format->fourcc == DRM_FORMAT_NV21) 198 ddcr2 |= PnDDCR2_NV21; 199 200 ddcr2 |= PnDDCR2_DIVU; 201 } else { 202 ddcr2 |= PnDDCR2_DIVY; 203 } 204 } 205 206 rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2); 207 rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4); 208 209 /* Destination position and size */ 210 rcar_du_plane_write(rgrp, index, PnDSXR, plane->plane.state->crtc_w); 211 rcar_du_plane_write(rgrp, index, PnDSYR, plane->plane.state->crtc_h); 212 rcar_du_plane_write(rgrp, index, PnDPXR, plane->plane.state->crtc_x); 213 rcar_du_plane_write(rgrp, index, PnDPYR, plane->plane.state->crtc_y); 214 215 /* Wrap-around and blinking, disabled */ 216 rcar_du_plane_write(rgrp, index, PnWASPR, 0); 217 rcar_du_plane_write(rgrp, index, PnWAMWR, 4095); 218 rcar_du_plane_write(rgrp, index, PnBTR, 0); 219 rcar_du_plane_write(rgrp, index, PnMLR, 0); 220} 221 222void rcar_du_plane_setup(struct rcar_du_plane *plane) 223{ 224 struct rcar_du_plane_state *state = 225 to_rcar_du_plane_state(plane->plane.state); 226 227 __rcar_du_plane_setup(plane, state->hwindex); 228 if (state->format->planes == 2) 229 __rcar_du_plane_setup(plane, (state->hwindex + 1) % 8); 230 231 rcar_du_plane_setup_fb(plane); 232} 233 234static int rcar_du_plane_atomic_check(struct drm_plane *plane, 235 struct drm_plane_state *state) 236{ 237 struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state); 238 struct rcar_du_plane *rplane = to_rcar_plane(plane); 239 struct rcar_du_device *rcdu = rplane->group->dev; 240 241 if (!state->fb || !state->crtc) { 242 rstate->format = NULL; 243 return 0; 244 } 245 246 if (state->src_w >> 16 != state->crtc_w || 247 state->src_h >> 16 != state->crtc_h) { 248 dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__); 249 return -EINVAL; 250 } 251 252 rstate->format = rcar_du_format_info(state->fb->pixel_format); 253 if (rstate->format == NULL) { 254 dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, 255 state->fb->pixel_format); 256 return -EINVAL; 257 } 258 259 return 0; 260} 261 262static void rcar_du_plane_atomic_update(struct drm_plane *plane, 263 struct drm_plane_state *old_state) 264{ 265 struct rcar_du_plane *rplane = to_rcar_plane(plane); 266 267 if (plane->state->crtc) 268 rcar_du_plane_setup(rplane); 269} 270 271static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { 272 .atomic_check = rcar_du_plane_atomic_check, 273 .atomic_update = rcar_du_plane_atomic_update, 274}; 275 276static void rcar_du_plane_reset(struct drm_plane *plane) 277{ 278 struct rcar_du_plane_state *state; 279 280 if (plane->state && plane->state->fb) 281 drm_framebuffer_unreference(plane->state->fb); 282 283 kfree(plane->state); 284 plane->state = NULL; 285 286 state = kzalloc(sizeof(*state), GFP_KERNEL); 287 if (state == NULL) 288 return; 289 290 state->hwindex = -1; 291 state->alpha = 255; 292 state->colorkey = RCAR_DU_COLORKEY_NONE; 293 state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; 294 295 plane->state = &state->state; 296 plane->state->plane = plane; 297} 298 299static struct drm_plane_state * 300rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane) 301{ 302 struct rcar_du_plane_state *state; 303 struct rcar_du_plane_state *copy; 304 305 state = to_rcar_du_plane_state(plane->state); 306 copy = kmemdup(state, sizeof(*state), GFP_KERNEL); 307 if (copy == NULL) 308 return NULL; 309 310 if (copy->state.fb) 311 drm_framebuffer_reference(copy->state.fb); 312 313 return ©->state; 314} 315 316static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane, 317 struct drm_plane_state *state) 318{ 319 if (state->fb) 320 drm_framebuffer_unreference(state->fb); 321 322 kfree(to_rcar_du_plane_state(state)); 323} 324 325static int rcar_du_plane_atomic_set_property(struct drm_plane *plane, 326 struct drm_plane_state *state, 327 struct drm_property *property, 328 uint64_t val) 329{ 330 struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state); 331 struct rcar_du_plane *rplane = to_rcar_plane(plane); 332 struct rcar_du_group *rgrp = rplane->group; 333 334 if (property == rgrp->planes.alpha) 335 rstate->alpha = val; 336 else if (property == rgrp->planes.colorkey) 337 rstate->colorkey = val; 338 else if (property == rgrp->planes.zpos) 339 rstate->zpos = val; 340 else 341 return -EINVAL; 342 343 return 0; 344} 345 346static int rcar_du_plane_atomic_get_property(struct drm_plane *plane, 347 const struct drm_plane_state *state, struct drm_property *property, 348 uint64_t *val) 349{ 350 const struct rcar_du_plane_state *rstate = 351 container_of(state, const struct rcar_du_plane_state, state); 352 struct rcar_du_plane *rplane = to_rcar_plane(plane); 353 struct rcar_du_group *rgrp = rplane->group; 354 355 if (property == rgrp->planes.alpha) 356 *val = rstate->alpha; 357 else if (property == rgrp->planes.colorkey) 358 *val = rstate->colorkey; 359 else if (property == rgrp->planes.zpos) 360 *val = rstate->zpos; 361 else 362 return -EINVAL; 363 364 return 0; 365} 366 367static const struct drm_plane_funcs rcar_du_plane_funcs = { 368 .update_plane = drm_atomic_helper_update_plane, 369 .disable_plane = drm_atomic_helper_disable_plane, 370 .reset = rcar_du_plane_reset, 371 .set_property = drm_atomic_helper_plane_set_property, 372 .destroy = drm_plane_cleanup, 373 .atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state, 374 .atomic_destroy_state = rcar_du_plane_atomic_destroy_state, 375 .atomic_set_property = rcar_du_plane_atomic_set_property, 376 .atomic_get_property = rcar_du_plane_atomic_get_property, 377}; 378 379static const uint32_t formats[] = { 380 DRM_FORMAT_RGB565, 381 DRM_FORMAT_ARGB1555, 382 DRM_FORMAT_XRGB1555, 383 DRM_FORMAT_XRGB8888, 384 DRM_FORMAT_ARGB8888, 385 DRM_FORMAT_UYVY, 386 DRM_FORMAT_YUYV, 387 DRM_FORMAT_NV12, 388 DRM_FORMAT_NV21, 389 DRM_FORMAT_NV16, 390}; 391 392int rcar_du_planes_init(struct rcar_du_group *rgrp) 393{ 394 struct rcar_du_planes *planes = &rgrp->planes; 395 struct rcar_du_device *rcdu = rgrp->dev; 396 unsigned int num_planes; 397 unsigned int num_crtcs; 398 unsigned int crtcs; 399 unsigned int i; 400 int ret; 401 402 planes->alpha = 403 drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255); 404 if (planes->alpha == NULL) 405 return -ENOMEM; 406 407 /* The color key is expressed as an RGB888 triplet stored in a 32-bit 408 * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0) 409 * or enable source color keying (1). 410 */ 411 planes->colorkey = 412 drm_property_create_range(rcdu->ddev, 0, "colorkey", 413 0, 0x01ffffff); 414 if (planes->colorkey == NULL) 415 return -ENOMEM; 416 417 planes->zpos = 418 drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7); 419 if (planes->zpos == NULL) 420 return -ENOMEM; 421 422 /* Create one primary plane per in this group CRTC and seven overlay 423 * planes. 424 */ 425 num_crtcs = min(rcdu->num_crtcs - 2 * rgrp->index, 2U); 426 num_planes = num_crtcs + 7; 427 428 crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index)); 429 430 for (i = 0; i < num_planes; ++i) { 431 enum drm_plane_type type = i < num_crtcs 432 ? DRM_PLANE_TYPE_PRIMARY 433 : DRM_PLANE_TYPE_OVERLAY; 434 struct rcar_du_plane *plane = &planes->planes[i]; 435 436 plane->group = rgrp; 437 438 ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs, 439 &rcar_du_plane_funcs, formats, 440 ARRAY_SIZE(formats), type); 441 if (ret < 0) 442 return ret; 443 444 drm_plane_helper_add(&plane->plane, 445 &rcar_du_plane_helper_funcs); 446 447 if (type == DRM_PLANE_TYPE_PRIMARY) 448 continue; 449 450 drm_object_attach_property(&plane->plane.base, 451 planes->alpha, 255); 452 drm_object_attach_property(&plane->plane.base, 453 planes->colorkey, 454 RCAR_DU_COLORKEY_NONE); 455 drm_object_attach_property(&plane->plane.base, 456 planes->zpos, 1); 457 } 458 459 return 0; 460} 461