347 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * RZ/G2L Display Unit VSP-Based Compositor4 *5 * Copyright (C) 2023 Renesas Electronics Corporation6 *7 * Based on rcar_du_vsp.c8 */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 34static 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 47void 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 61void rzg2l_du_vsp_disable(struct rzg2l_du_crtc *crtc)62{63 vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);64}65 66void 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 73struct 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 89static 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 117static 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 175static 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 only185 * updated by drm_atomic_helper_check_plane_state, set it186 * 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 214static 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 224static 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 239static 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 244static struct drm_plane_state *245rzg2l_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 261static 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 268static 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 284static 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 292static 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 299int 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_crtcs326 ? DRM_PLANE_TYPE_PRIMARY327 : 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