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_atomic_helper.h> 10 #include <drm/drm_gem_cma_helper.h> 11 12 #include "dcss-dev.h" 13 #include "dcss-kms.h" 14 15 static 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 35 static const u64 dcss_video_format_modifiers[] = { 36 DRM_FORMAT_MOD_LINEAR, 37 DRM_FORMAT_MOD_INVALID, 38 }; 39 40 static 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 47 static inline struct dcss_plane *to_dcss_plane(struct drm_plane *p) 48 { 49 return container_of(p, struct dcss_plane, base); 50 } 51 52 static 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 59 static 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 68 static 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 92 static 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 102 static 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 || modifier == DRM_FORMAT_MOD_LINEAR; 107 u32 supported_rotation = DRM_MODE_ROTATE_0; 108 109 if (!format->is_yuv && linear_format) 110 supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 | 111 DRM_MODE_REFLECT_MASK; 112 else if (!format->is_yuv && 113 (modifier == DRM_FORMAT_MOD_VIVANTE_TILED || 114 modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED)) 115 supported_rotation = DRM_MODE_ROTATE_MASK | 116 DRM_MODE_REFLECT_MASK; 117 else if (format->is_yuv && linear_format && 118 (format->format == DRM_FORMAT_NV12 || 119 format->format == DRM_FORMAT_NV21)) 120 supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 | 121 DRM_MODE_REFLECT_MASK; 122 123 return !!(rotation & supported_rotation); 124 } 125 126 static bool dcss_plane_is_source_size_allowed(u16 src_w, u16 src_h, u32 pix_fmt) 127 { 128 if (src_w < 64 && 129 (pix_fmt == DRM_FORMAT_NV12 || pix_fmt == DRM_FORMAT_NV21)) 130 return false; 131 else if (src_w < 32 && 132 (pix_fmt == DRM_FORMAT_UYVY || pix_fmt == DRM_FORMAT_VYUY || 133 pix_fmt == DRM_FORMAT_YUYV || pix_fmt == DRM_FORMAT_YVYU)) 134 return false; 135 136 return src_w >= 16 && src_h >= 8; 137 } 138 139 static int dcss_plane_atomic_check(struct drm_plane *plane, 140 struct drm_atomic_state *state) 141 { 142 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 143 plane); 144 struct dcss_plane *dcss_plane = to_dcss_plane(plane); 145 struct dcss_dev *dcss = plane->dev->dev_private; 146 struct drm_framebuffer *fb = new_plane_state->fb; 147 bool is_primary_plane = plane->type == DRM_PLANE_TYPE_PRIMARY; 148 struct drm_gem_cma_object *cma_obj; 149 struct drm_crtc_state *crtc_state; 150 int hdisplay, vdisplay; 151 int min, max; 152 int ret; 153 154 if (!fb || !new_plane_state->crtc) 155 return 0; 156 157 cma_obj = drm_fb_cma_get_gem_obj(fb, 0); 158 WARN_ON(!cma_obj); 159 160 crtc_state = drm_atomic_get_existing_crtc_state(state, 161 new_plane_state->crtc); 162 163 hdisplay = crtc_state->adjusted_mode.hdisplay; 164 vdisplay = crtc_state->adjusted_mode.vdisplay; 165 166 if (!dcss_plane_is_source_size_allowed(new_plane_state->src_w >> 16, 167 new_plane_state->src_h >> 16, 168 fb->format->format)) { 169 DRM_DEBUG_KMS("Source plane size is not allowed!\n"); 170 return -EINVAL; 171 } 172 173 dcss_scaler_get_min_max_ratios(dcss->scaler, dcss_plane->ch_num, 174 &min, &max); 175 176 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 177 min, max, !is_primary_plane, 178 false); 179 if (ret) 180 return ret; 181 182 if (!new_plane_state->visible) 183 return 0; 184 185 if (!dcss_plane_can_rotate(fb->format, 186 !!(fb->flags & DRM_MODE_FB_MODIFIERS), 187 fb->modifier, 188 new_plane_state->rotation)) { 189 DRM_DEBUG_KMS("requested rotation is not allowed!\n"); 190 return -EINVAL; 191 } 192 193 if ((new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0 || 194 new_plane_state->crtc_x + new_plane_state->crtc_w > hdisplay || 195 new_plane_state->crtc_y + new_plane_state->crtc_h > vdisplay) && 196 !dcss_plane_fb_is_linear(fb)) { 197 DRM_DEBUG_KMS("requested cropping operation is not allowed!\n"); 198 return -EINVAL; 199 } 200 201 if ((fb->flags & DRM_MODE_FB_MODIFIERS) && 202 !plane->funcs->format_mod_supported(plane, 203 fb->format->format, 204 fb->modifier)) { 205 DRM_DEBUG_KMS("Invalid modifier: %llx", fb->modifier); 206 return -EINVAL; 207 } 208 209 return 0; 210 } 211 212 static void dcss_plane_atomic_set_base(struct dcss_plane *dcss_plane) 213 { 214 struct drm_plane *plane = &dcss_plane->base; 215 struct drm_plane_state *state = plane->state; 216 struct dcss_dev *dcss = plane->dev->dev_private; 217 struct drm_framebuffer *fb = state->fb; 218 const struct drm_format_info *format = fb->format; 219 struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); 220 unsigned long p1_ba = 0, p2_ba = 0; 221 222 if (!format->is_yuv || 223 format->format == DRM_FORMAT_NV12 || 224 format->format == DRM_FORMAT_NV21) 225 p1_ba = cma_obj->paddr + fb->offsets[0] + 226 fb->pitches[0] * (state->src.y1 >> 16) + 227 format->char_per_block[0] * (state->src.x1 >> 16); 228 else if (format->format == DRM_FORMAT_UYVY || 229 format->format == DRM_FORMAT_VYUY || 230 format->format == DRM_FORMAT_YUYV || 231 format->format == DRM_FORMAT_YVYU) 232 p1_ba = cma_obj->paddr + fb->offsets[0] + 233 fb->pitches[0] * (state->src.y1 >> 16) + 234 2 * format->char_per_block[0] * (state->src.x1 >> 17); 235 236 if (format->format == DRM_FORMAT_NV12 || 237 format->format == DRM_FORMAT_NV21) 238 p2_ba = cma_obj->paddr + fb->offsets[1] + 239 (((fb->pitches[1] >> 1) * (state->src.y1 >> 17) + 240 (state->src.x1 >> 17)) << 1); 241 242 dcss_dpr_addr_set(dcss->dpr, dcss_plane->ch_num, p1_ba, p2_ba, 243 fb->pitches[0]); 244 } 245 246 static bool dcss_plane_needs_setup(struct drm_plane_state *state, 247 struct drm_plane_state *old_state) 248 { 249 struct drm_framebuffer *fb = state->fb; 250 struct drm_framebuffer *old_fb = old_state->fb; 251 252 return state->crtc_x != old_state->crtc_x || 253 state->crtc_y != old_state->crtc_y || 254 state->crtc_w != old_state->crtc_w || 255 state->crtc_h != old_state->crtc_h || 256 state->src_x != old_state->src_x || 257 state->src_y != old_state->src_y || 258 state->src_w != old_state->src_w || 259 state->src_h != old_state->src_h || 260 fb->format->format != old_fb->format->format || 261 fb->modifier != old_fb->modifier || 262 state->rotation != old_state->rotation || 263 state->scaling_filter != old_state->scaling_filter; 264 } 265 266 static void dcss_plane_atomic_update(struct drm_plane *plane, 267 struct drm_atomic_state *state) 268 { 269 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 270 plane); 271 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 272 plane); 273 struct dcss_plane *dcss_plane = to_dcss_plane(plane); 274 struct dcss_dev *dcss = plane->dev->dev_private; 275 struct drm_framebuffer *fb = new_state->fb; 276 struct drm_crtc_state *crtc_state; 277 bool modifiers_present; 278 u32 src_w, src_h, dst_w, dst_h; 279 struct drm_rect src, dst; 280 bool enable = true; 281 bool is_rotation_90_or_270; 282 283 if (!fb || !new_state->crtc || !new_state->visible) 284 return; 285 286 crtc_state = new_state->crtc->state; 287 modifiers_present = !!(fb->flags & DRM_MODE_FB_MODIFIERS); 288 289 if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state) && 290 !dcss_plane_needs_setup(new_state, old_state)) { 291 dcss_plane_atomic_set_base(dcss_plane); 292 return; 293 } 294 295 src = plane->state->src; 296 dst = plane->state->dst; 297 298 /* 299 * The width and height after clipping. 300 */ 301 src_w = drm_rect_width(&src) >> 16; 302 src_h = drm_rect_height(&src) >> 16; 303 dst_w = drm_rect_width(&dst); 304 dst_h = drm_rect_height(&dst); 305 306 if (plane->type == DRM_PLANE_TYPE_OVERLAY && 307 modifiers_present && fb->modifier == DRM_FORMAT_MOD_LINEAR) 308 modifiers_present = false; 309 310 dcss_dpr_format_set(dcss->dpr, dcss_plane->ch_num, 311 new_state->fb->format, 312 modifiers_present ? fb->modifier : 313 DRM_FORMAT_MOD_LINEAR); 314 dcss_dpr_set_res(dcss->dpr, dcss_plane->ch_num, src_w, src_h); 315 dcss_dpr_set_rotation(dcss->dpr, dcss_plane->ch_num, 316 new_state->rotation); 317 318 dcss_plane_atomic_set_base(dcss_plane); 319 320 is_rotation_90_or_270 = new_state->rotation & (DRM_MODE_ROTATE_90 | 321 DRM_MODE_ROTATE_270); 322 323 dcss_scaler_set_filter(dcss->scaler, dcss_plane->ch_num, 324 new_state->scaling_filter); 325 326 dcss_scaler_setup(dcss->scaler, dcss_plane->ch_num, 327 new_state->fb->format, 328 is_rotation_90_or_270 ? src_h : src_w, 329 is_rotation_90_or_270 ? src_w : src_h, 330 dst_w, dst_h, 331 drm_mode_vrefresh(&crtc_state->mode)); 332 333 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 334 dst.x1, dst.y1, dst_w, dst_h); 335 dcss_dtg_plane_alpha_set(dcss->dtg, dcss_plane->ch_num, 336 fb->format, new_state->alpha >> 8); 337 338 if (!dcss_plane->ch_num && (new_state->alpha >> 8) == 0) 339 enable = false; 340 341 dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, enable); 342 dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, enable); 343 344 if (!enable) 345 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 346 0, 0, 0, 0); 347 348 dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, enable); 349 } 350 351 static void dcss_plane_atomic_disable(struct drm_plane *plane, 352 struct drm_atomic_state *state) 353 { 354 struct dcss_plane *dcss_plane = to_dcss_plane(plane); 355 struct dcss_dev *dcss = plane->dev->dev_private; 356 357 dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, false); 358 dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, false); 359 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 0, 0, 0, 0); 360 dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, false); 361 } 362 363 static const struct drm_plane_helper_funcs dcss_plane_helper_funcs = { 364 .atomic_check = dcss_plane_atomic_check, 365 .atomic_update = dcss_plane_atomic_update, 366 .atomic_disable = dcss_plane_atomic_disable, 367 }; 368 369 struct dcss_plane *dcss_plane_init(struct drm_device *drm, 370 unsigned int possible_crtcs, 371 enum drm_plane_type type, 372 unsigned int zpos) 373 { 374 struct dcss_plane *dcss_plane; 375 const u64 *format_modifiers = dcss_video_format_modifiers; 376 int ret; 377 378 if (zpos > 2) 379 return ERR_PTR(-EINVAL); 380 381 dcss_plane = kzalloc(sizeof(*dcss_plane), GFP_KERNEL); 382 if (!dcss_plane) { 383 DRM_ERROR("failed to allocate plane\n"); 384 return ERR_PTR(-ENOMEM); 385 } 386 387 if (type == DRM_PLANE_TYPE_PRIMARY) 388 format_modifiers = dcss_graphics_format_modifiers; 389 390 ret = drm_universal_plane_init(drm, &dcss_plane->base, possible_crtcs, 391 &dcss_plane_funcs, dcss_common_formats, 392 ARRAY_SIZE(dcss_common_formats), 393 format_modifiers, type, NULL); 394 if (ret) { 395 DRM_ERROR("failed to initialize plane\n"); 396 kfree(dcss_plane); 397 return ERR_PTR(ret); 398 } 399 400 drm_plane_helper_add(&dcss_plane->base, &dcss_plane_helper_funcs); 401 402 ret = drm_plane_create_zpos_immutable_property(&dcss_plane->base, zpos); 403 if (ret) 404 return ERR_PTR(ret); 405 406 drm_plane_create_scaling_filter_property(&dcss_plane->base, 407 BIT(DRM_SCALING_FILTER_DEFAULT) | 408 BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR)); 409 410 drm_plane_create_rotation_property(&dcss_plane->base, 411 DRM_MODE_ROTATE_0, 412 DRM_MODE_ROTATE_0 | 413 DRM_MODE_ROTATE_90 | 414 DRM_MODE_ROTATE_180 | 415 DRM_MODE_ROTATE_270 | 416 DRM_MODE_REFLECT_X | 417 DRM_MODE_REFLECT_Y); 418 419 dcss_plane->ch_num = zpos; 420 421 return dcss_plane; 422 } 423