1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * RZ/G2L Display Unit VSP-Based Compositor 4 * 5 * Copyright (C) 2023 Renesas Electronics Corporation 6 * 7 * Based on rcar_du_vsp.c 8 */ 9 10 #include <drm/drm_atomic.h> 11 #include <drm/drm_atomic_helper.h> 12 #include <drm/drm_blend.h> 13 #include <drm/drm_crtc.h> 14 #include <drm/drm_fb_dma_helper.h> 15 #include <drm/drm_fourcc.h> 16 #include <drm/drm_framebuffer.h> 17 #include <drm/drm_gem_atomic_helper.h> 18 #include <drm/drm_gem_dma_helper.h> 19 #include <drm/drm_managed.h> 20 #include <drm/drm_vblank.h> 21 22 #include <linux/bitops.h> 23 #include <linux/dma-mapping.h> 24 #include <linux/of_platform.h> 25 #include <linux/platform_device.h> 26 #include <linux/scatterlist.h> 27 28 #include <media/vsp1.h> 29 30 #include "rzg2l_du_drv.h" 31 #include "rzg2l_du_kms.h" 32 #include "rzg2l_du_vsp.h" 33 34 static void rzg2l_du_vsp_complete(void *private, unsigned int status, u32 crc) 35 { 36 struct rzg2l_du_crtc *crtc = private; 37 38 if (crtc->vblank_enable) 39 drm_crtc_handle_vblank(&crtc->crtc); 40 41 if (status & VSP1_DU_STATUS_COMPLETE) 42 rzg2l_du_crtc_finish_page_flip(crtc); 43 44 drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc); 45 } 46 47 void rzg2l_du_vsp_enable(struct rzg2l_du_crtc *crtc) 48 { 49 const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode; 50 struct vsp1_du_lif_config cfg = { 51 .width = mode->hdisplay, 52 .height = mode->vdisplay, 53 .interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE, 54 .callback = rzg2l_du_vsp_complete, 55 .callback_data = crtc, 56 }; 57 58 vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); 59 } 60 61 void rzg2l_du_vsp_disable(struct rzg2l_du_crtc *crtc) 62 { 63 vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL); 64 } 65 66 void rzg2l_du_vsp_atomic_flush(struct rzg2l_du_crtc *crtc) 67 { 68 struct vsp1_du_atomic_pipe_config cfg = { { 0, } }; 69 70 vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); 71 } 72 73 struct drm_plane *rzg2l_du_vsp_get_drm_plane(struct rzg2l_du_crtc *crtc, 74 unsigned int pipe_index) 75 { 76 struct rzg2l_du_device *rcdu = crtc->vsp->dev; 77 struct drm_plane *plane = NULL; 78 79 drm_for_each_plane(plane, &rcdu->ddev) { 80 struct rzg2l_du_vsp_plane *vsp_plane = to_rzg2l_vsp_plane(plane); 81 82 if (vsp_plane->index == pipe_index) 83 break; 84 } 85 86 return plane ? plane : ERR_PTR(-EINVAL); 87 } 88 89 static const u32 rzg2l_du_vsp_formats[] = { 90 DRM_FORMAT_RGB332, 91 DRM_FORMAT_ARGB4444, 92 DRM_FORMAT_XRGB4444, 93 DRM_FORMAT_ARGB1555, 94 DRM_FORMAT_XRGB1555, 95 DRM_FORMAT_RGB565, 96 DRM_FORMAT_BGR888, 97 DRM_FORMAT_RGB888, 98 DRM_FORMAT_BGRA8888, 99 DRM_FORMAT_BGRX8888, 100 DRM_FORMAT_ARGB8888, 101 DRM_FORMAT_XRGB8888, 102 DRM_FORMAT_UYVY, 103 DRM_FORMAT_YUYV, 104 DRM_FORMAT_YVYU, 105 DRM_FORMAT_NV12, 106 DRM_FORMAT_NV21, 107 DRM_FORMAT_NV16, 108 DRM_FORMAT_NV61, 109 DRM_FORMAT_YUV420, 110 DRM_FORMAT_YVU420, 111 DRM_FORMAT_YUV422, 112 DRM_FORMAT_YVU422, 113 DRM_FORMAT_YUV444, 114 DRM_FORMAT_YVU444, 115 }; 116 117 static void rzg2l_du_vsp_plane_setup(struct rzg2l_du_vsp_plane *plane) 118 { 119 struct rzg2l_du_vsp_plane_state *state = 120 to_rzg2l_vsp_plane_state(plane->plane.state); 121 struct rzg2l_du_crtc *crtc = to_rzg2l_crtc(state->state.crtc); 122 struct drm_framebuffer *fb = plane->plane.state->fb; 123 const struct rzg2l_du_format_info *format; 124 struct vsp1_du_atomic_config cfg = { 125 .pixelformat = 0, 126 .pitch = fb->pitches[0], 127 .alpha = state->state.alpha >> 8, 128 .zpos = state->state.zpos, 129 }; 130 u32 fourcc = state->format->fourcc; 131 unsigned int i; 132 133 cfg.src.left = state->state.src.x1 >> 16; 134 cfg.src.top = state->state.src.y1 >> 16; 135 cfg.src.width = drm_rect_width(&state->state.src) >> 16; 136 cfg.src.height = drm_rect_height(&state->state.src) >> 16; 137 138 cfg.dst.left = state->state.dst.x1; 139 cfg.dst.top = state->state.dst.y1; 140 cfg.dst.width = drm_rect_width(&state->state.dst); 141 cfg.dst.height = drm_rect_height(&state->state.dst); 142 143 for (i = 0; i < state->format->planes; ++i) { 144 struct drm_gem_dma_object *gem; 145 146 gem = drm_fb_dma_get_gem_obj(fb, i); 147 cfg.mem[i] = gem->dma_addr + fb->offsets[i]; 148 } 149 150 if (state->state.pixel_blend_mode == DRM_MODE_BLEND_PIXEL_NONE) { 151 switch (fourcc) { 152 case DRM_FORMAT_ARGB1555: 153 fourcc = DRM_FORMAT_XRGB1555; 154 break; 155 156 case DRM_FORMAT_ARGB4444: 157 fourcc = DRM_FORMAT_XRGB4444; 158 break; 159 160 case DRM_FORMAT_ARGB8888: 161 fourcc = DRM_FORMAT_XRGB8888; 162 break; 163 } 164 } 165 166 format = rzg2l_du_format_info(fourcc); 167 cfg.pixelformat = format->v4l2; 168 169 cfg.premult = state->state.pixel_blend_mode == DRM_MODE_BLEND_PREMULTI; 170 171 vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe, 172 plane->index, &cfg); 173 } 174 175 static int __rzg2l_du_vsp_plane_atomic_check(struct drm_plane *plane, 176 struct drm_plane_state *state, 177 const struct rzg2l_du_format_info **format) 178 { 179 struct drm_crtc_state *crtc_state; 180 int ret; 181 182 if (!state->crtc) { 183 /* 184 * The visible field is not reset by the DRM core but only 185 * updated by drm_atomic_helper_check_plane_state, set it 186 * manually. 187 */ 188 state->visible = false; 189 *format = NULL; 190 return 0; 191 } 192 193 crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); 194 if (IS_ERR(crtc_state)) 195 return PTR_ERR(crtc_state); 196 197 ret = drm_atomic_helper_check_plane_state(state, crtc_state, 198 DRM_PLANE_NO_SCALING, 199 DRM_PLANE_NO_SCALING, 200 true, true); 201 if (ret < 0) 202 return ret; 203 204 if (!state->visible) { 205 *format = NULL; 206 return 0; 207 } 208 209 *format = rzg2l_du_format_info(state->fb->format->format); 210 211 return 0; 212 } 213 214 static int rzg2l_du_vsp_plane_atomic_check(struct drm_plane *plane, 215 struct drm_atomic_state *state) 216 { 217 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 218 plane); 219 struct rzg2l_du_vsp_plane_state *rstate = to_rzg2l_vsp_plane_state(new_plane_state); 220 221 return __rzg2l_du_vsp_plane_atomic_check(plane, new_plane_state, &rstate->format); 222 } 223 224 static void rzg2l_du_vsp_plane_atomic_update(struct drm_plane *plane, 225 struct drm_atomic_state *state) 226 { 227 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); 228 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); 229 struct rzg2l_du_vsp_plane *rplane = to_rzg2l_vsp_plane(plane); 230 struct rzg2l_du_crtc *crtc = to_rzg2l_crtc(old_state->crtc); 231 232 if (new_state->visible) 233 rzg2l_du_vsp_plane_setup(rplane); 234 else if (old_state->crtc) 235 vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe, 236 rplane->index, NULL); 237 } 238 239 static const struct drm_plane_helper_funcs rzg2l_du_vsp_plane_helper_funcs = { 240 .atomic_check = rzg2l_du_vsp_plane_atomic_check, 241 .atomic_update = rzg2l_du_vsp_plane_atomic_update, 242 }; 243 244 static struct drm_plane_state * 245 rzg2l_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane) 246 { 247 struct rzg2l_du_vsp_plane_state *copy; 248 249 if (WARN_ON(!plane->state)) 250 return NULL; 251 252 copy = kzalloc(sizeof(*copy), GFP_KERNEL); 253 if (!copy) 254 return NULL; 255 256 __drm_atomic_helper_plane_duplicate_state(plane, ©->state); 257 258 return ©->state; 259 } 260 261 static void rzg2l_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane, 262 struct drm_plane_state *state) 263 { 264 __drm_atomic_helper_plane_destroy_state(state); 265 kfree(to_rzg2l_vsp_plane_state(state)); 266 } 267 268 static void rzg2l_du_vsp_plane_reset(struct drm_plane *plane) 269 { 270 struct rzg2l_du_vsp_plane_state *state; 271 272 if (plane->state) { 273 rzg2l_du_vsp_plane_atomic_destroy_state(plane, plane->state); 274 plane->state = NULL; 275 } 276 277 state = kzalloc(sizeof(*state), GFP_KERNEL); 278 if (!state) 279 return; 280 281 __drm_atomic_helper_plane_reset(plane, &state->state); 282 } 283 284 static const struct drm_plane_funcs rzg2l_du_vsp_plane_funcs = { 285 .update_plane = drm_atomic_helper_update_plane, 286 .disable_plane = drm_atomic_helper_disable_plane, 287 .reset = rzg2l_du_vsp_plane_reset, 288 .atomic_duplicate_state = rzg2l_du_vsp_plane_atomic_duplicate_state, 289 .atomic_destroy_state = rzg2l_du_vsp_plane_atomic_destroy_state, 290 }; 291 292 static void rzg2l_du_vsp_cleanup(struct drm_device *dev, void *res) 293 { 294 struct rzg2l_du_vsp *vsp = res; 295 296 put_device(vsp->vsp); 297 } 298 299 int rzg2l_du_vsp_init(struct rzg2l_du_vsp *vsp, struct device_node *np, 300 unsigned int crtcs) 301 { 302 struct rzg2l_du_device *rcdu = vsp->dev; 303 struct platform_device *pdev; 304 unsigned int num_crtcs = hweight32(crtcs); 305 unsigned int num_planes = 2; 306 unsigned int i; 307 int ret; 308 309 /* Find the VSP device and initialize it. */ 310 pdev = of_find_device_by_node(np); 311 if (!pdev) 312 return -ENXIO; 313 314 vsp->vsp = &pdev->dev; 315 316 ret = drmm_add_action_or_reset(&rcdu->ddev, rzg2l_du_vsp_cleanup, vsp); 317 if (ret < 0) 318 return ret; 319 320 ret = vsp1_du_init(vsp->vsp); 321 if (ret < 0) 322 return ret; 323 324 for (i = 0; i < num_planes; ++i) { 325 enum drm_plane_type type = i < num_crtcs 326 ? DRM_PLANE_TYPE_PRIMARY 327 : DRM_PLANE_TYPE_OVERLAY; 328 struct rzg2l_du_vsp_plane *plane; 329 330 plane = drmm_universal_plane_alloc(&rcdu->ddev, struct rzg2l_du_vsp_plane, 331 plane, crtcs, &rzg2l_du_vsp_plane_funcs, 332 rzg2l_du_vsp_formats, 333 ARRAY_SIZE(rzg2l_du_vsp_formats), 334 NULL, type, NULL); 335 if (IS_ERR(plane)) 336 return PTR_ERR(plane); 337 338 plane->vsp = vsp; 339 plane->index = i; 340 341 drm_plane_helper_add(&plane->plane, 342 &rzg2l_du_vsp_plane_helper_funcs); 343 } 344 345 return 0; 346 } 347