1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright 2019 NXP. 4 */ 5 6#include <drm/drm_atomic.h> 7#include <drm/drm_atomic_helper.h> 8#include <drm/drm_fb_cma_helper.h> 9#include <drm/drm_gem_framebuffer_helper.h> 10#include <drm/drm_gem_cma_helper.h> 11 12#include "dcss-dev.h" 13#include "dcss-kms.h" 14 15static const u32 dcss_common_formats[] = { 16 /* RGB */ 17 DRM_FORMAT_ARGB8888, 18 DRM_FORMAT_XRGB8888, 19 DRM_FORMAT_ABGR8888, 20 DRM_FORMAT_XBGR8888, 21 DRM_FORMAT_RGBA8888, 22 DRM_FORMAT_RGBX8888, 23 DRM_FORMAT_BGRA8888, 24 DRM_FORMAT_BGRX8888, 25 DRM_FORMAT_XRGB2101010, 26 DRM_FORMAT_XBGR2101010, 27 DRM_FORMAT_RGBX1010102, 28 DRM_FORMAT_BGRX1010102, 29 DRM_FORMAT_ARGB2101010, 30 DRM_FORMAT_ABGR2101010, 31 DRM_FORMAT_RGBA1010102, 32 DRM_FORMAT_BGRA1010102, 33}; 34 35static const u64 dcss_video_format_modifiers[] = { 36 DRM_FORMAT_MOD_LINEAR, 37 DRM_FORMAT_MOD_INVALID, 38}; 39 40static const u64 dcss_graphics_format_modifiers[] = { 41 DRM_FORMAT_MOD_VIVANTE_TILED, 42 DRM_FORMAT_MOD_VIVANTE_SUPER_TILED, 43 DRM_FORMAT_MOD_LINEAR, 44 DRM_FORMAT_MOD_INVALID, 45}; 46 47static inline struct dcss_plane *to_dcss_plane(struct drm_plane *p) 48{ 49 return container_of(p, struct dcss_plane, base); 50} 51 52static inline bool dcss_plane_fb_is_linear(const struct drm_framebuffer *fb) 53{ 54 return ((fb->flags & DRM_MODE_FB_MODIFIERS) == 0) || 55 ((fb->flags & DRM_MODE_FB_MODIFIERS) != 0 && 56 fb->modifier == DRM_FORMAT_MOD_LINEAR); 57} 58 59static void dcss_plane_destroy(struct drm_plane *plane) 60{ 61 struct dcss_plane *dcss_plane = container_of(plane, struct dcss_plane, 62 base); 63 64 drm_plane_cleanup(plane); 65 kfree(dcss_plane); 66} 67 68static bool dcss_plane_format_mod_supported(struct drm_plane *plane, 69 u32 format, 70 u64 modifier) 71{ 72 switch (plane->type) { 73 case DRM_PLANE_TYPE_PRIMARY: 74 switch (format) { 75 case DRM_FORMAT_ARGB8888: 76 case DRM_FORMAT_XRGB8888: 77 case DRM_FORMAT_ARGB2101010: 78 return modifier == DRM_FORMAT_MOD_LINEAR || 79 modifier == DRM_FORMAT_MOD_VIVANTE_TILED || 80 modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED; 81 default: 82 return modifier == DRM_FORMAT_MOD_LINEAR; 83 } 84 break; 85 case DRM_PLANE_TYPE_OVERLAY: 86 return modifier == DRM_FORMAT_MOD_LINEAR; 87 default: 88 return false; 89 } 90} 91 92static const struct drm_plane_funcs dcss_plane_funcs = { 93 .update_plane = drm_atomic_helper_update_plane, 94 .disable_plane = drm_atomic_helper_disable_plane, 95 .destroy = dcss_plane_destroy, 96 .reset = drm_atomic_helper_plane_reset, 97 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 98 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 99 .format_mod_supported = dcss_plane_format_mod_supported, 100}; 101 102static bool dcss_plane_can_rotate(const struct drm_format_info *format, 103 bool mod_present, u64 modifier, 104 unsigned int rotation) 105{ 106 bool linear_format = !mod_present || 107 (mod_present && modifier == DRM_FORMAT_MOD_LINEAR); 108 u32 supported_rotation = DRM_MODE_ROTATE_0; 109 110 if (!format->is_yuv && linear_format) 111 supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 | 112 DRM_MODE_REFLECT_MASK; 113 else if (!format->is_yuv && 114 (modifier == DRM_FORMAT_MOD_VIVANTE_TILED || 115 modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED)) 116 supported_rotation = DRM_MODE_ROTATE_MASK | 117 DRM_MODE_REFLECT_MASK; 118 else if (format->is_yuv && linear_format && 119 (format->format == DRM_FORMAT_NV12 || 120 format->format == DRM_FORMAT_NV21)) 121 supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 | 122 DRM_MODE_REFLECT_MASK; 123 124 return !!(rotation & supported_rotation); 125} 126 127static bool dcss_plane_is_source_size_allowed(u16 src_w, u16 src_h, u32 pix_fmt) 128{ 129 if (src_w < 64 && 130 (pix_fmt == DRM_FORMAT_NV12 || pix_fmt == DRM_FORMAT_NV21)) 131 return false; 132 else if (src_w < 32 && 133 (pix_fmt == DRM_FORMAT_UYVY || pix_fmt == DRM_FORMAT_VYUY || 134 pix_fmt == DRM_FORMAT_YUYV || pix_fmt == DRM_FORMAT_YVYU)) 135 return false; 136 137 return src_w >= 16 && src_h >= 8; 138} 139 140static int dcss_plane_atomic_check(struct drm_plane *plane, 141 struct drm_plane_state *state) 142{ 143 struct dcss_plane *dcss_plane = to_dcss_plane(plane); 144 struct dcss_dev *dcss = plane->dev->dev_private; 145 struct drm_framebuffer *fb = state->fb; 146 bool is_primary_plane = plane->type == DRM_PLANE_TYPE_PRIMARY; 147 struct drm_gem_cma_object *cma_obj; 148 struct drm_crtc_state *crtc_state; 149 int hdisplay, vdisplay; 150 int min, max; 151 int ret; 152 153 if (!fb || !state->crtc) 154 return 0; 155 156 cma_obj = drm_fb_cma_get_gem_obj(fb, 0); 157 WARN_ON(!cma_obj); 158 159 crtc_state = drm_atomic_get_existing_crtc_state(state->state, 160 state->crtc); 161 162 hdisplay = crtc_state->adjusted_mode.hdisplay; 163 vdisplay = crtc_state->adjusted_mode.vdisplay; 164 165 if (!dcss_plane_is_source_size_allowed(state->src_w >> 16, 166 state->src_h >> 16, 167 fb->format->format)) { 168 DRM_DEBUG_KMS("Source plane size is not allowed!\n"); 169 return -EINVAL; 170 } 171 172 dcss_scaler_get_min_max_ratios(dcss->scaler, dcss_plane->ch_num, 173 &min, &max); 174 175 ret = drm_atomic_helper_check_plane_state(state, crtc_state, 176 min, max, !is_primary_plane, 177 false); 178 if (ret) 179 return ret; 180 181 if (!state->visible) 182 return 0; 183 184 if (!dcss_plane_can_rotate(fb->format, 185 !!(fb->flags & DRM_MODE_FB_MODIFIERS), 186 fb->modifier, 187 state->rotation)) { 188 DRM_DEBUG_KMS("requested rotation is not allowed!\n"); 189 return -EINVAL; 190 } 191 192 if ((state->crtc_x < 0 || state->crtc_y < 0 || 193 state->crtc_x + state->crtc_w > hdisplay || 194 state->crtc_y + state->crtc_h > vdisplay) && 195 !dcss_plane_fb_is_linear(fb)) { 196 DRM_DEBUG_KMS("requested cropping operation is not allowed!\n"); 197 return -EINVAL; 198 } 199 200 if ((fb->flags & DRM_MODE_FB_MODIFIERS) && 201 !plane->funcs->format_mod_supported(plane, 202 fb->format->format, 203 fb->modifier)) { 204 DRM_DEBUG_KMS("Invalid modifier: %llx", fb->modifier); 205 return -EINVAL; 206 } 207 208 return 0; 209} 210 211static void dcss_plane_atomic_set_base(struct dcss_plane *dcss_plane) 212{ 213 struct drm_plane *plane = &dcss_plane->base; 214 struct drm_plane_state *state = plane->state; 215 struct dcss_dev *dcss = plane->dev->dev_private; 216 struct drm_framebuffer *fb = state->fb; 217 const struct drm_format_info *format = fb->format; 218 struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); 219 unsigned long p1_ba = 0, p2_ba = 0; 220 221 if (!format->is_yuv || 222 format->format == DRM_FORMAT_NV12 || 223 format->format == DRM_FORMAT_NV21) 224 p1_ba = cma_obj->paddr + fb->offsets[0] + 225 fb->pitches[0] * (state->src.y1 >> 16) + 226 format->char_per_block[0] * (state->src.x1 >> 16); 227 else if (format->format == DRM_FORMAT_UYVY || 228 format->format == DRM_FORMAT_VYUY || 229 format->format == DRM_FORMAT_YUYV || 230 format->format == DRM_FORMAT_YVYU) 231 p1_ba = cma_obj->paddr + fb->offsets[0] + 232 fb->pitches[0] * (state->src.y1 >> 16) + 233 2 * format->char_per_block[0] * (state->src.x1 >> 17); 234 235 if (format->format == DRM_FORMAT_NV12 || 236 format->format == DRM_FORMAT_NV21) 237 p2_ba = cma_obj->paddr + fb->offsets[1] + 238 (((fb->pitches[1] >> 1) * (state->src.y1 >> 17) + 239 (state->src.x1 >> 17)) << 1); 240 241 dcss_dpr_addr_set(dcss->dpr, dcss_plane->ch_num, p1_ba, p2_ba, 242 fb->pitches[0]); 243} 244 245static bool dcss_plane_needs_setup(struct drm_plane_state *state, 246 struct drm_plane_state *old_state) 247{ 248 struct drm_framebuffer *fb = state->fb; 249 struct drm_framebuffer *old_fb = old_state->fb; 250 251 return state->crtc_x != old_state->crtc_x || 252 state->crtc_y != old_state->crtc_y || 253 state->crtc_w != old_state->crtc_w || 254 state->crtc_h != old_state->crtc_h || 255 state->src_x != old_state->src_x || 256 state->src_y != old_state->src_y || 257 state->src_w != old_state->src_w || 258 state->src_h != old_state->src_h || 259 fb->format->format != old_fb->format->format || 260 fb->modifier != old_fb->modifier || 261 state->rotation != old_state->rotation; 262} 263 264static void dcss_plane_atomic_update(struct drm_plane *plane, 265 struct drm_plane_state *old_state) 266{ 267 struct drm_plane_state *state = plane->state; 268 struct dcss_plane *dcss_plane = to_dcss_plane(plane); 269 struct dcss_dev *dcss = plane->dev->dev_private; 270 struct drm_framebuffer *fb = state->fb; 271 struct drm_crtc_state *crtc_state; 272 bool modifiers_present; 273 u32 src_w, src_h, dst_w, dst_h; 274 struct drm_rect src, dst; 275 bool enable = true; 276 bool is_rotation_90_or_270; 277 278 if (!fb || !state->crtc || !state->visible) 279 return; 280 281 crtc_state = state->crtc->state; 282 modifiers_present = !!(fb->flags & DRM_MODE_FB_MODIFIERS); 283 284 if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state) && 285 !dcss_plane_needs_setup(state, old_state)) { 286 dcss_plane_atomic_set_base(dcss_plane); 287 return; 288 } 289 290 src = plane->state->src; 291 dst = plane->state->dst; 292 293 /* 294 * The width and height after clipping. 295 */ 296 src_w = drm_rect_width(&src) >> 16; 297 src_h = drm_rect_height(&src) >> 16; 298 dst_w = drm_rect_width(&dst); 299 dst_h = drm_rect_height(&dst); 300 301 if (plane->type == DRM_PLANE_TYPE_OVERLAY && 302 modifiers_present && fb->modifier == DRM_FORMAT_MOD_LINEAR) 303 modifiers_present = false; 304 305 dcss_dpr_format_set(dcss->dpr, dcss_plane->ch_num, state->fb->format, 306 modifiers_present ? fb->modifier : 307 DRM_FORMAT_MOD_LINEAR); 308 dcss_dpr_set_res(dcss->dpr, dcss_plane->ch_num, src_w, src_h); 309 dcss_dpr_set_rotation(dcss->dpr, dcss_plane->ch_num, 310 state->rotation); 311 312 dcss_plane_atomic_set_base(dcss_plane); 313 314 is_rotation_90_or_270 = state->rotation & (DRM_MODE_ROTATE_90 | 315 DRM_MODE_ROTATE_270); 316 317 dcss_scaler_setup(dcss->scaler, dcss_plane->ch_num, 318 state->fb->format, 319 is_rotation_90_or_270 ? src_h : src_w, 320 is_rotation_90_or_270 ? src_w : src_h, 321 dst_w, dst_h, 322 drm_mode_vrefresh(&crtc_state->mode)); 323 324 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 325 dst.x1, dst.y1, dst_w, dst_h); 326 dcss_dtg_plane_alpha_set(dcss->dtg, dcss_plane->ch_num, 327 fb->format, state->alpha >> 8); 328 329 if (!dcss_plane->ch_num && (state->alpha >> 8) == 0) 330 enable = false; 331 332 dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, enable); 333 dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, enable); 334 335 if (!enable) 336 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 337 0, 0, 0, 0); 338 339 dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, enable); 340} 341 342static void dcss_plane_atomic_disable(struct drm_plane *plane, 343 struct drm_plane_state *old_state) 344{ 345 struct dcss_plane *dcss_plane = to_dcss_plane(plane); 346 struct dcss_dev *dcss = plane->dev->dev_private; 347 348 dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, false); 349 dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, false); 350 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 0, 0, 0, 0); 351 dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, false); 352} 353 354static const struct drm_plane_helper_funcs dcss_plane_helper_funcs = { 355 .prepare_fb = drm_gem_fb_prepare_fb, 356 .atomic_check = dcss_plane_atomic_check, 357 .atomic_update = dcss_plane_atomic_update, 358 .atomic_disable = dcss_plane_atomic_disable, 359}; 360 361struct dcss_plane *dcss_plane_init(struct drm_device *drm, 362 unsigned int possible_crtcs, 363 enum drm_plane_type type, 364 unsigned int zpos) 365{ 366 struct dcss_plane *dcss_plane; 367 const u64 *format_modifiers = dcss_video_format_modifiers; 368 int ret; 369 370 if (zpos > 2) 371 return ERR_PTR(-EINVAL); 372 373 dcss_plane = kzalloc(sizeof(*dcss_plane), GFP_KERNEL); 374 if (!dcss_plane) { 375 DRM_ERROR("failed to allocate plane\n"); 376 return ERR_PTR(-ENOMEM); 377 } 378 379 if (type == DRM_PLANE_TYPE_PRIMARY) 380 format_modifiers = dcss_graphics_format_modifiers; 381 382 ret = drm_universal_plane_init(drm, &dcss_plane->base, possible_crtcs, 383 &dcss_plane_funcs, dcss_common_formats, 384 ARRAY_SIZE(dcss_common_formats), 385 format_modifiers, type, NULL); 386 if (ret) { 387 DRM_ERROR("failed to initialize plane\n"); 388 kfree(dcss_plane); 389 return ERR_PTR(ret); 390 } 391 392 drm_plane_helper_add(&dcss_plane->base, &dcss_plane_helper_funcs); 393 394 ret = drm_plane_create_zpos_immutable_property(&dcss_plane->base, zpos); 395 if (ret) 396 return ERR_PTR(ret); 397 398 drm_plane_create_rotation_property(&dcss_plane->base, 399 DRM_MODE_ROTATE_0, 400 DRM_MODE_ROTATE_0 | 401 DRM_MODE_ROTATE_90 | 402 DRM_MODE_ROTATE_180 | 403 DRM_MODE_ROTATE_270 | 404 DRM_MODE_REFLECT_X | 405 DRM_MODE_REFLECT_Y); 406 407 dcss_plane->ch_num = zpos; 408 409 return dcss_plane; 410} 411