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