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 struct rzg2l_du_crtc_state *state; 70 71 state = to_rzg2l_crtc_state(crtc->crtc.state); 72 73 vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg); 74 } 75 76 struct drm_plane *rzg2l_du_vsp_get_drm_plane(struct rzg2l_du_crtc *crtc, 77 unsigned int pipe_index) 78 { 79 struct rzg2l_du_device *rcdu = crtc->vsp->dev; 80 struct drm_plane *plane = NULL; 81 82 drm_for_each_plane(plane, &rcdu->ddev) { 83 struct rzg2l_du_vsp_plane *vsp_plane = to_rzg2l_vsp_plane(plane); 84 85 if (vsp_plane->index == pipe_index) 86 break; 87 } 88 89 return plane ? plane : ERR_PTR(-EINVAL); 90 } 91 92 static const u32 rzg2l_du_vsp_formats[] = { 93 DRM_FORMAT_RGB332, 94 DRM_FORMAT_ARGB4444, 95 DRM_FORMAT_XRGB4444, 96 DRM_FORMAT_ARGB1555, 97 DRM_FORMAT_XRGB1555, 98 DRM_FORMAT_RGB565, 99 DRM_FORMAT_BGR888, 100 DRM_FORMAT_RGB888, 101 DRM_FORMAT_BGRA8888, 102 DRM_FORMAT_BGRX8888, 103 DRM_FORMAT_ARGB8888, 104 DRM_FORMAT_XRGB8888, 105 DRM_FORMAT_UYVY, 106 DRM_FORMAT_YUYV, 107 DRM_FORMAT_YVYU, 108 DRM_FORMAT_NV12, 109 DRM_FORMAT_NV21, 110 DRM_FORMAT_NV16, 111 DRM_FORMAT_NV61, 112 DRM_FORMAT_YUV420, 113 DRM_FORMAT_YVU420, 114 DRM_FORMAT_YUV422, 115 DRM_FORMAT_YVU422, 116 DRM_FORMAT_YUV444, 117 DRM_FORMAT_YVU444, 118 }; 119 120 static void rzg2l_du_vsp_plane_setup(struct rzg2l_du_vsp_plane *plane) 121 { 122 struct rzg2l_du_vsp_plane_state *state = 123 to_rzg2l_vsp_plane_state(plane->plane.state); 124 struct rzg2l_du_crtc *crtc = to_rzg2l_crtc(state->state.crtc); 125 struct drm_framebuffer *fb = plane->plane.state->fb; 126 const struct rzg2l_du_format_info *format; 127 struct vsp1_du_atomic_config cfg = { 128 .pixelformat = 0, 129 .pitch = fb->pitches[0], 130 .alpha = state->state.alpha >> 8, 131 .zpos = state->state.zpos, 132 }; 133 u32 fourcc = state->format->fourcc; 134 unsigned int i; 135 136 cfg.src.left = state->state.src.x1 >> 16; 137 cfg.src.top = state->state.src.y1 >> 16; 138 cfg.src.width = drm_rect_width(&state->state.src) >> 16; 139 cfg.src.height = drm_rect_height(&state->state.src) >> 16; 140 141 cfg.dst.left = state->state.dst.x1; 142 cfg.dst.top = state->state.dst.y1; 143 cfg.dst.width = drm_rect_width(&state->state.dst); 144 cfg.dst.height = drm_rect_height(&state->state.dst); 145 146 for (i = 0; i < state->format->planes; ++i) { 147 struct drm_gem_dma_object *gem; 148 149 gem = drm_fb_dma_get_gem_obj(fb, i); 150 cfg.mem[i] = gem->dma_addr + fb->offsets[i]; 151 } 152 153 if (state->state.pixel_blend_mode == DRM_MODE_BLEND_PIXEL_NONE) { 154 switch (fourcc) { 155 case DRM_FORMAT_ARGB1555: 156 fourcc = DRM_FORMAT_XRGB1555; 157 break; 158 159 case DRM_FORMAT_ARGB4444: 160 fourcc = DRM_FORMAT_XRGB4444; 161 break; 162 163 case DRM_FORMAT_ARGB8888: 164 fourcc = DRM_FORMAT_XRGB8888; 165 break; 166 } 167 } 168 169 format = rzg2l_du_format_info(fourcc); 170 cfg.pixelformat = format->v4l2; 171 172 cfg.premult = state->state.pixel_blend_mode == DRM_MODE_BLEND_PREMULTI; 173 174 vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe, 175 plane->index, &cfg); 176 } 177 178 static int __rzg2l_du_vsp_plane_atomic_check(struct drm_plane *plane, 179 struct drm_plane_state *state, 180 const struct rzg2l_du_format_info **format) 181 { 182 struct drm_crtc_state *crtc_state; 183 int ret; 184 185 if (!state->crtc) { 186 /* 187 * The visible field is not reset by the DRM core but only 188 * updated by drm_atomic_helper_check_plane_state, set it 189 * manually. 190 */ 191 state->visible = false; 192 *format = NULL; 193 return 0; 194 } 195 196 crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); 197 if (IS_ERR(crtc_state)) 198 return PTR_ERR(crtc_state); 199 200 ret = drm_atomic_helper_check_plane_state(state, crtc_state, 201 DRM_PLANE_NO_SCALING, 202 DRM_PLANE_NO_SCALING, 203 true, true); 204 if (ret < 0) 205 return ret; 206 207 if (!state->visible) { 208 *format = NULL; 209 return 0; 210 } 211 212 *format = rzg2l_du_format_info(state->fb->format->format); 213 214 return 0; 215 } 216 217 static int rzg2l_du_vsp_plane_atomic_check(struct drm_plane *plane, 218 struct drm_atomic_state *state) 219 { 220 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 221 plane); 222 struct rzg2l_du_vsp_plane_state *rstate = to_rzg2l_vsp_plane_state(new_plane_state); 223 224 return __rzg2l_du_vsp_plane_atomic_check(plane, new_plane_state, &rstate->format); 225 } 226 227 static void rzg2l_du_vsp_plane_atomic_update(struct drm_plane *plane, 228 struct drm_atomic_state *state) 229 { 230 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); 231 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); 232 struct rzg2l_du_vsp_plane *rplane = to_rzg2l_vsp_plane(plane); 233 struct rzg2l_du_crtc *crtc = to_rzg2l_crtc(old_state->crtc); 234 235 if (new_state->visible) 236 rzg2l_du_vsp_plane_setup(rplane); 237 else if (old_state->crtc) 238 vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe, 239 rplane->index, NULL); 240 } 241 242 static const struct drm_plane_helper_funcs rzg2l_du_vsp_plane_helper_funcs = { 243 .atomic_check = rzg2l_du_vsp_plane_atomic_check, 244 .atomic_update = rzg2l_du_vsp_plane_atomic_update, 245 }; 246 247 static struct drm_plane_state * 248 rzg2l_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane) 249 { 250 struct rzg2l_du_vsp_plane_state *copy; 251 252 if (WARN_ON(!plane->state)) 253 return NULL; 254 255 copy = kzalloc(sizeof(*copy), GFP_KERNEL); 256 if (!copy) 257 return NULL; 258 259 __drm_atomic_helper_plane_duplicate_state(plane, ©->state); 260 261 return ©->state; 262 } 263 264 static void rzg2l_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane, 265 struct drm_plane_state *state) 266 { 267 __drm_atomic_helper_plane_destroy_state(state); 268 kfree(to_rzg2l_vsp_plane_state(state)); 269 } 270 271 static void rzg2l_du_vsp_plane_reset(struct drm_plane *plane) 272 { 273 struct rzg2l_du_vsp_plane_state *state; 274 275 if (plane->state) { 276 rzg2l_du_vsp_plane_atomic_destroy_state(plane, plane->state); 277 plane->state = NULL; 278 } 279 280 state = kzalloc(sizeof(*state), GFP_KERNEL); 281 if (!state) 282 return; 283 284 __drm_atomic_helper_plane_reset(plane, &state->state); 285 } 286 287 static const struct drm_plane_funcs rzg2l_du_vsp_plane_funcs = { 288 .update_plane = drm_atomic_helper_update_plane, 289 .disable_plane = drm_atomic_helper_disable_plane, 290 .reset = rzg2l_du_vsp_plane_reset, 291 .atomic_duplicate_state = rzg2l_du_vsp_plane_atomic_duplicate_state, 292 .atomic_destroy_state = rzg2l_du_vsp_plane_atomic_destroy_state, 293 }; 294 295 static void rzg2l_du_vsp_cleanup(struct drm_device *dev, void *res) 296 { 297 struct rzg2l_du_vsp *vsp = res; 298 299 put_device(vsp->vsp); 300 } 301 302 int rzg2l_du_vsp_init(struct rzg2l_du_vsp *vsp, struct device_node *np, 303 unsigned int crtcs) 304 { 305 struct rzg2l_du_device *rcdu = vsp->dev; 306 struct platform_device *pdev; 307 unsigned int num_crtcs = hweight32(crtcs); 308 unsigned int num_planes = 2; 309 unsigned int i; 310 int ret; 311 312 /* Find the VSP device and initialize it. */ 313 pdev = of_find_device_by_node(np); 314 if (!pdev) 315 return -ENXIO; 316 317 vsp->vsp = &pdev->dev; 318 319 ret = drmm_add_action_or_reset(&rcdu->ddev, rzg2l_du_vsp_cleanup, vsp); 320 if (ret < 0) 321 return ret; 322 323 ret = vsp1_du_init(vsp->vsp); 324 if (ret < 0) 325 return ret; 326 327 for (i = 0; i < num_planes; ++i) { 328 enum drm_plane_type type = i < num_crtcs 329 ? DRM_PLANE_TYPE_PRIMARY 330 : DRM_PLANE_TYPE_OVERLAY; 331 struct rzg2l_du_vsp_plane *plane; 332 333 plane = drmm_universal_plane_alloc(&rcdu->ddev, struct rzg2l_du_vsp_plane, 334 plane, crtcs, &rzg2l_du_vsp_plane_funcs, 335 rzg2l_du_vsp_formats, 336 ARRAY_SIZE(rzg2l_du_vsp_formats), 337 NULL, type, NULL); 338 if (IS_ERR(plane)) 339 return PTR_ERR(plane); 340 341 plane->vsp = vsp; 342 plane->index = i; 343 344 drm_plane_helper_add(&plane->plane, 345 &rzg2l_du_vsp_plane_helper_funcs); 346 } 347 348 return 0; 349 } 350