rcar_du_vsp.c (11631B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * rcar_du_vsp.h -- R-Car Display Unit VSP-Based Compositor 4 * 5 * Copyright (C) 2015 Renesas Electronics Corporation 6 * 7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10#include <drm/drm_atomic.h> 11#include <drm/drm_atomic_helper.h> 12#include <drm/drm_crtc.h> 13#include <drm/drm_fb_cma_helper.h> 14#include <drm/drm_fourcc.h> 15#include <drm/drm_gem_atomic_helper.h> 16#include <drm/drm_gem_cma_helper.h> 17#include <drm/drm_managed.h> 18#include <drm/drm_plane_helper.h> 19#include <drm/drm_vblank.h> 20 21#include <linux/bitops.h> 22#include <linux/dma-mapping.h> 23#include <linux/of_platform.h> 24#include <linux/scatterlist.h> 25#include <linux/slab.h> 26#include <linux/videodev2.h> 27 28#include <media/vsp1.h> 29 30#include "rcar_du_drv.h" 31#include "rcar_du_kms.h" 32#include "rcar_du_vsp.h" 33#include "rcar_du_writeback.h" 34 35static void rcar_du_vsp_complete(void *private, unsigned int status, u32 crc) 36{ 37 struct rcar_du_crtc *crtc = private; 38 39 if (crtc->vblank_enable) 40 drm_crtc_handle_vblank(&crtc->crtc); 41 42 if (status & VSP1_DU_STATUS_COMPLETE) 43 rcar_du_crtc_finish_page_flip(crtc); 44 if (status & VSP1_DU_STATUS_WRITEBACK) 45 rcar_du_writeback_complete(crtc); 46 47 drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc); 48} 49 50void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) 51{ 52 const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode; 53 struct rcar_du_device *rcdu = crtc->dev; 54 struct vsp1_du_lif_config cfg = { 55 .width = mode->hdisplay, 56 .height = mode->vdisplay, 57 .interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE, 58 .callback = rcar_du_vsp_complete, 59 .callback_data = crtc, 60 }; 61 struct rcar_du_plane_state state = { 62 .state = { 63 .alpha = DRM_BLEND_ALPHA_OPAQUE, 64 .crtc = &crtc->crtc, 65 .dst.x1 = 0, 66 .dst.y1 = 0, 67 .dst.x2 = mode->hdisplay, 68 .dst.y2 = mode->vdisplay, 69 .src.x1 = 0, 70 .src.y1 = 0, 71 .src.x2 = mode->hdisplay << 16, 72 .src.y2 = mode->vdisplay << 16, 73 .zpos = 0, 74 }, 75 .format = rcar_du_format_info(DRM_FORMAT_ARGB8888), 76 .source = RCAR_DU_PLANE_VSPD1, 77 .colorkey = 0, 78 }; 79 80 if (rcdu->info->gen >= 3) 81 state.hwindex = (crtc->index % 2) ? 2 : 0; 82 else 83 state.hwindex = crtc->index % 2; 84 85 __rcar_du_plane_setup(crtc->group, &state); 86 87 vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); 88} 89 90void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) 91{ 92 vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL); 93} 94 95void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) 96{ 97 vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe); 98} 99 100void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc) 101{ 102 struct vsp1_du_atomic_pipe_config cfg = { { 0, } }; 103 struct rcar_du_crtc_state *state; 104 105 state = to_rcar_crtc_state(crtc->crtc.state); 106 cfg.crc = state->crc; 107 108 rcar_du_writeback_setup(crtc, &cfg.writeback); 109 110 vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); 111} 112 113static const u32 rcar_du_vsp_formats[] = { 114 DRM_FORMAT_RGB332, 115 DRM_FORMAT_ARGB4444, 116 DRM_FORMAT_XRGB4444, 117 DRM_FORMAT_ARGB1555, 118 DRM_FORMAT_XRGB1555, 119 DRM_FORMAT_RGB565, 120 DRM_FORMAT_BGR888, 121 DRM_FORMAT_RGB888, 122 DRM_FORMAT_BGRA8888, 123 DRM_FORMAT_BGRX8888, 124 DRM_FORMAT_ARGB8888, 125 DRM_FORMAT_XRGB8888, 126 DRM_FORMAT_UYVY, 127 DRM_FORMAT_YUYV, 128 DRM_FORMAT_YVYU, 129 DRM_FORMAT_NV12, 130 DRM_FORMAT_NV21, 131 DRM_FORMAT_NV16, 132 DRM_FORMAT_NV61, 133 DRM_FORMAT_YUV420, 134 DRM_FORMAT_YVU420, 135 DRM_FORMAT_YUV422, 136 DRM_FORMAT_YVU422, 137 DRM_FORMAT_YUV444, 138 DRM_FORMAT_YVU444, 139}; 140 141static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane) 142{ 143 struct rcar_du_vsp_plane_state *state = 144 to_rcar_vsp_plane_state(plane->plane.state); 145 struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc); 146 struct drm_framebuffer *fb = plane->plane.state->fb; 147 const struct rcar_du_format_info *format; 148 struct vsp1_du_atomic_config cfg = { 149 .pixelformat = 0, 150 .pitch = fb->pitches[0], 151 .alpha = state->state.alpha >> 8, 152 .zpos = state->state.zpos, 153 }; 154 unsigned int i; 155 156 cfg.src.left = state->state.src.x1 >> 16; 157 cfg.src.top = state->state.src.y1 >> 16; 158 cfg.src.width = drm_rect_width(&state->state.src) >> 16; 159 cfg.src.height = drm_rect_height(&state->state.src) >> 16; 160 161 cfg.dst.left = state->state.dst.x1; 162 cfg.dst.top = state->state.dst.y1; 163 cfg.dst.width = drm_rect_width(&state->state.dst); 164 cfg.dst.height = drm_rect_height(&state->state.dst); 165 166 for (i = 0; i < state->format->planes; ++i) 167 cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl) 168 + fb->offsets[i]; 169 170 format = rcar_du_format_info(state->format->fourcc); 171 cfg.pixelformat = format->v4l2; 172 173 vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe, 174 plane->index, &cfg); 175} 176 177int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb, 178 struct sg_table sg_tables[3]) 179{ 180 struct rcar_du_device *rcdu = vsp->dev; 181 unsigned int i, j; 182 int ret; 183 184 for (i = 0; i < fb->format->num_planes; ++i) { 185 struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i); 186 struct sg_table *sgt = &sg_tables[i]; 187 188 if (gem->sgt) { 189 struct scatterlist *src; 190 struct scatterlist *dst; 191 192 /* 193 * If the GEM buffer has a scatter gather table, it has 194 * been imported from a dma-buf and has no physical 195 * address as it might not be physically contiguous. 196 * Copy the original scatter gather table to map it to 197 * the VSP. 198 */ 199 ret = sg_alloc_table(sgt, gem->sgt->orig_nents, 200 GFP_KERNEL); 201 if (ret) 202 goto fail; 203 204 src = gem->sgt->sgl; 205 dst = sgt->sgl; 206 for (j = 0; j < gem->sgt->orig_nents; ++j) { 207 sg_set_page(dst, sg_page(src), src->length, 208 src->offset); 209 src = sg_next(src); 210 dst = sg_next(dst); 211 } 212 } else { 213 ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr, 214 gem->paddr, gem->base.size); 215 if (ret) 216 goto fail; 217 } 218 219 ret = vsp1_du_map_sg(vsp->vsp, sgt); 220 if (ret) { 221 sg_free_table(sgt); 222 goto fail; 223 } 224 } 225 226 return 0; 227 228fail: 229 while (i--) { 230 struct sg_table *sgt = &sg_tables[i]; 231 232 vsp1_du_unmap_sg(vsp->vsp, sgt); 233 sg_free_table(sgt); 234 } 235 236 return ret; 237} 238 239static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane, 240 struct drm_plane_state *state) 241{ 242 struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state); 243 struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp; 244 int ret; 245 246 /* 247 * There's no need to prepare (and unprepare) the framebuffer when the 248 * plane is not visible, as it will not be displayed. 249 */ 250 if (!state->visible) 251 return 0; 252 253 ret = rcar_du_vsp_map_fb(vsp, state->fb, rstate->sg_tables); 254 if (ret < 0) 255 return ret; 256 257 return drm_gem_plane_helper_prepare_fb(plane, state); 258} 259 260void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb, 261 struct sg_table sg_tables[3]) 262{ 263 unsigned int i; 264 265 for (i = 0; i < fb->format->num_planes; ++i) { 266 struct sg_table *sgt = &sg_tables[i]; 267 268 vsp1_du_unmap_sg(vsp->vsp, sgt); 269 sg_free_table(sgt); 270 } 271} 272 273static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane, 274 struct drm_plane_state *state) 275{ 276 struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state); 277 struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp; 278 279 if (!state->visible) 280 return; 281 282 rcar_du_vsp_unmap_fb(vsp, state->fb, rstate->sg_tables); 283} 284 285static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane, 286 struct drm_atomic_state *state) 287{ 288 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 289 plane); 290 struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(new_plane_state); 291 292 return __rcar_du_plane_atomic_check(plane, new_plane_state, 293 &rstate->format); 294} 295 296static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane, 297 struct drm_atomic_state *state) 298{ 299 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); 300 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); 301 struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane); 302 struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc); 303 304 if (new_state->visible) 305 rcar_du_vsp_plane_setup(rplane); 306 else if (old_state->crtc) 307 vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe, 308 rplane->index, NULL); 309} 310 311static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = { 312 .prepare_fb = rcar_du_vsp_plane_prepare_fb, 313 .cleanup_fb = rcar_du_vsp_plane_cleanup_fb, 314 .atomic_check = rcar_du_vsp_plane_atomic_check, 315 .atomic_update = rcar_du_vsp_plane_atomic_update, 316}; 317 318static struct drm_plane_state * 319rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane) 320{ 321 struct rcar_du_vsp_plane_state *copy; 322 323 if (WARN_ON(!plane->state)) 324 return NULL; 325 326 copy = kzalloc(sizeof(*copy), GFP_KERNEL); 327 if (copy == NULL) 328 return NULL; 329 330 __drm_atomic_helper_plane_duplicate_state(plane, ©->state); 331 332 return ©->state; 333} 334 335static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane, 336 struct drm_plane_state *state) 337{ 338 __drm_atomic_helper_plane_destroy_state(state); 339 kfree(to_rcar_vsp_plane_state(state)); 340} 341 342static void rcar_du_vsp_plane_reset(struct drm_plane *plane) 343{ 344 struct rcar_du_vsp_plane_state *state; 345 346 if (plane->state) { 347 rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state); 348 plane->state = NULL; 349 } 350 351 state = kzalloc(sizeof(*state), GFP_KERNEL); 352 if (state == NULL) 353 return; 354 355 __drm_atomic_helper_plane_reset(plane, &state->state); 356} 357 358static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = { 359 .update_plane = drm_atomic_helper_update_plane, 360 .disable_plane = drm_atomic_helper_disable_plane, 361 .reset = rcar_du_vsp_plane_reset, 362 .destroy = drm_plane_cleanup, 363 .atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state, 364 .atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state, 365}; 366 367static void rcar_du_vsp_cleanup(struct drm_device *dev, void *res) 368{ 369 struct rcar_du_vsp *vsp = res; 370 unsigned int i; 371 372 for (i = 0; i < vsp->num_planes; ++i) { 373 struct rcar_du_vsp_plane *plane = &vsp->planes[i]; 374 375 drm_plane_cleanup(&plane->plane); 376 } 377 378 kfree(vsp->planes); 379 380 put_device(vsp->vsp); 381} 382 383int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np, 384 unsigned int crtcs) 385{ 386 struct rcar_du_device *rcdu = vsp->dev; 387 struct platform_device *pdev; 388 unsigned int num_crtcs = hweight32(crtcs); 389 unsigned int num_planes; 390 unsigned int i; 391 int ret; 392 393 /* Find the VSP device and initialize it. */ 394 pdev = of_find_device_by_node(np); 395 if (!pdev) 396 return -ENXIO; 397 398 vsp->vsp = &pdev->dev; 399 400 ret = drmm_add_action_or_reset(&rcdu->ddev, rcar_du_vsp_cleanup, vsp); 401 if (ret < 0) 402 return ret; 403 404 ret = vsp1_du_init(vsp->vsp); 405 if (ret < 0) 406 return ret; 407 408 /* 409 * The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to 410 * 4 RPFs. 411 */ 412 num_planes = rcdu->info->gen >= 3 ? 5 : 4; 413 414 vsp->planes = kcalloc(num_planes, sizeof(*vsp->planes), GFP_KERNEL); 415 if (!vsp->planes) 416 return -ENOMEM; 417 418 for (i = 0; i < num_planes; ++i) { 419 enum drm_plane_type type = i < num_crtcs 420 ? DRM_PLANE_TYPE_PRIMARY 421 : DRM_PLANE_TYPE_OVERLAY; 422 struct rcar_du_vsp_plane *plane = &vsp->planes[i]; 423 424 plane->vsp = vsp; 425 plane->index = i; 426 427 ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane, 428 crtcs, &rcar_du_vsp_plane_funcs, 429 rcar_du_vsp_formats, 430 ARRAY_SIZE(rcar_du_vsp_formats), 431 NULL, type, NULL); 432 if (ret < 0) 433 return ret; 434 435 drm_plane_helper_add(&plane->plane, 436 &rcar_du_vsp_plane_helper_funcs); 437 438 if (type == DRM_PLANE_TYPE_PRIMARY) { 439 drm_plane_create_zpos_immutable_property(&plane->plane, 440 0); 441 } else { 442 drm_plane_create_alpha_property(&plane->plane); 443 drm_plane_create_zpos_property(&plane->plane, 1, 1, 444 num_planes - 1); 445 } 446 447 vsp->num_planes++; 448 } 449 450 return 0; 451}