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 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 || 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 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_plane_state *state) 141 { 142 struct dcss_plane *dcss_plane = to_dcss_plane(plane); 143 struct dcss_dev *dcss = plane->dev->dev_private; 144 struct drm_framebuffer *fb = state->fb; 145 bool is_primary_plane = plane->type == DRM_PLANE_TYPE_PRIMARY; 146 struct drm_gem_cma_object *cma_obj; 147 struct drm_crtc_state *crtc_state; 148 int hdisplay, vdisplay; 149 int min, max; 150 int ret; 151 152 if (!fb || !state->crtc) 153 return 0; 154 155 cma_obj = drm_fb_cma_get_gem_obj(fb, 0); 156 WARN_ON(!cma_obj); 157 158 crtc_state = drm_atomic_get_existing_crtc_state(state->state, 159 state->crtc); 160 161 hdisplay = crtc_state->adjusted_mode.hdisplay; 162 vdisplay = crtc_state->adjusted_mode.vdisplay; 163 164 if (!dcss_plane_is_source_size_allowed(state->src_w >> 16, 165 state->src_h >> 16, 166 fb->format->format)) { 167 DRM_DEBUG_KMS("Source plane size is not allowed!\n"); 168 return -EINVAL; 169 } 170 171 dcss_scaler_get_min_max_ratios(dcss->scaler, dcss_plane->ch_num, 172 &min, &max); 173 174 ret = drm_atomic_helper_check_plane_state(state, crtc_state, 175 min, max, !is_primary_plane, 176 false); 177 if (ret) 178 return ret; 179 180 if (!state->visible) 181 return 0; 182 183 if (!dcss_plane_can_rotate(fb->format, 184 !!(fb->flags & DRM_MODE_FB_MODIFIERS), 185 fb->modifier, 186 state->rotation)) { 187 DRM_DEBUG_KMS("requested rotation is not allowed!\n"); 188 return -EINVAL; 189 } 190 191 if ((state->crtc_x < 0 || state->crtc_y < 0 || 192 state->crtc_x + state->crtc_w > hdisplay || 193 state->crtc_y + state->crtc_h > vdisplay) && 194 !dcss_plane_fb_is_linear(fb)) { 195 DRM_DEBUG_KMS("requested cropping operation is not allowed!\n"); 196 return -EINVAL; 197 } 198 199 if ((fb->flags & DRM_MODE_FB_MODIFIERS) && 200 !plane->funcs->format_mod_supported(plane, 201 fb->format->format, 202 fb->modifier)) { 203 DRM_DEBUG_KMS("Invalid modifier: %llx", fb->modifier); 204 return -EINVAL; 205 } 206 207 return 0; 208 } 209 210 static void dcss_plane_atomic_set_base(struct dcss_plane *dcss_plane) 211 { 212 struct drm_plane *plane = &dcss_plane->base; 213 struct drm_plane_state *state = plane->state; 214 struct dcss_dev *dcss = plane->dev->dev_private; 215 struct drm_framebuffer *fb = state->fb; 216 const struct drm_format_info *format = fb->format; 217 struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); 218 unsigned long p1_ba = 0, p2_ba = 0; 219 220 if (!format->is_yuv || 221 format->format == DRM_FORMAT_NV12 || 222 format->format == DRM_FORMAT_NV21) 223 p1_ba = cma_obj->paddr + fb->offsets[0] + 224 fb->pitches[0] * (state->src.y1 >> 16) + 225 format->char_per_block[0] * (state->src.x1 >> 16); 226 else if (format->format == DRM_FORMAT_UYVY || 227 format->format == DRM_FORMAT_VYUY || 228 format->format == DRM_FORMAT_YUYV || 229 format->format == DRM_FORMAT_YVYU) 230 p1_ba = cma_obj->paddr + fb->offsets[0] + 231 fb->pitches[0] * (state->src.y1 >> 16) + 232 2 * format->char_per_block[0] * (state->src.x1 >> 17); 233 234 if (format->format == DRM_FORMAT_NV12 || 235 format->format == DRM_FORMAT_NV21) 236 p2_ba = cma_obj->paddr + fb->offsets[1] + 237 (((fb->pitches[1] >> 1) * (state->src.y1 >> 17) + 238 (state->src.x1 >> 17)) << 1); 239 240 dcss_dpr_addr_set(dcss->dpr, dcss_plane->ch_num, p1_ba, p2_ba, 241 fb->pitches[0]); 242 } 243 244 static bool dcss_plane_needs_setup(struct drm_plane_state *state, 245 struct drm_plane_state *old_state) 246 { 247 struct drm_framebuffer *fb = state->fb; 248 struct drm_framebuffer *old_fb = old_state->fb; 249 250 return state->crtc_x != old_state->crtc_x || 251 state->crtc_y != old_state->crtc_y || 252 state->crtc_w != old_state->crtc_w || 253 state->crtc_h != old_state->crtc_h || 254 state->src_x != old_state->src_x || 255 state->src_y != old_state->src_y || 256 state->src_w != old_state->src_w || 257 state->src_h != old_state->src_h || 258 fb->format->format != old_fb->format->format || 259 fb->modifier != old_fb->modifier || 260 state->rotation != old_state->rotation; 261 } 262 263 static void dcss_plane_atomic_update(struct drm_plane *plane, 264 struct drm_plane_state *old_state) 265 { 266 struct drm_plane_state *state = plane->state; 267 struct dcss_plane *dcss_plane = to_dcss_plane(plane); 268 struct dcss_dev *dcss = plane->dev->dev_private; 269 struct drm_framebuffer *fb = state->fb; 270 u32 pixel_format; 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 277 if (!fb || !state->crtc || !state->visible) 278 return; 279 280 pixel_format = state->fb->format->format; 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 dcss_scaler_setup(dcss->scaler, dcss_plane->ch_num, 315 state->fb->format, src_w, src_h, 316 dst_w, dst_h, 317 drm_mode_vrefresh(&crtc_state->mode)); 318 319 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 320 dst.x1, dst.y1, dst_w, dst_h); 321 dcss_dtg_plane_alpha_set(dcss->dtg, dcss_plane->ch_num, 322 fb->format, state->alpha >> 8); 323 324 if (!dcss_plane->ch_num && (state->alpha >> 8) == 0) 325 enable = false; 326 327 dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, enable); 328 dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, enable); 329 330 if (!enable) 331 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 332 0, 0, 0, 0); 333 334 dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, enable); 335 } 336 337 static void dcss_plane_atomic_disable(struct drm_plane *plane, 338 struct drm_plane_state *old_state) 339 { 340 struct dcss_plane *dcss_plane = to_dcss_plane(plane); 341 struct dcss_dev *dcss = plane->dev->dev_private; 342 343 dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, false); 344 dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, false); 345 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 0, 0, 0, 0); 346 dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, false); 347 } 348 349 static const struct drm_plane_helper_funcs dcss_plane_helper_funcs = { 350 .prepare_fb = drm_gem_fb_prepare_fb, 351 .atomic_check = dcss_plane_atomic_check, 352 .atomic_update = dcss_plane_atomic_update, 353 .atomic_disable = dcss_plane_atomic_disable, 354 }; 355 356 struct dcss_plane *dcss_plane_init(struct drm_device *drm, 357 unsigned int possible_crtcs, 358 enum drm_plane_type type, 359 unsigned int zpos) 360 { 361 struct dcss_plane *dcss_plane; 362 const u64 *format_modifiers = dcss_video_format_modifiers; 363 int ret; 364 365 if (zpos > 2) 366 return ERR_PTR(-EINVAL); 367 368 dcss_plane = kzalloc(sizeof(*dcss_plane), GFP_KERNEL); 369 if (!dcss_plane) { 370 DRM_ERROR("failed to allocate plane\n"); 371 return ERR_PTR(-ENOMEM); 372 } 373 374 if (type == DRM_PLANE_TYPE_PRIMARY) 375 format_modifiers = dcss_graphics_format_modifiers; 376 377 ret = drm_universal_plane_init(drm, &dcss_plane->base, possible_crtcs, 378 &dcss_plane_funcs, dcss_common_formats, 379 ARRAY_SIZE(dcss_common_formats), 380 format_modifiers, type, NULL); 381 if (ret) { 382 DRM_ERROR("failed to initialize plane\n"); 383 kfree(dcss_plane); 384 return ERR_PTR(ret); 385 } 386 387 drm_plane_helper_add(&dcss_plane->base, &dcss_plane_helper_funcs); 388 389 ret = drm_plane_create_zpos_immutable_property(&dcss_plane->base, zpos); 390 if (ret) 391 return ERR_PTR(ret); 392 393 drm_plane_create_rotation_property(&dcss_plane->base, 394 DRM_MODE_ROTATE_0, 395 DRM_MODE_ROTATE_0 | 396 DRM_MODE_ROTATE_90 | 397 DRM_MODE_ROTATE_180 | 398 DRM_MODE_ROTATE_270 | 399 DRM_MODE_REFLECT_X | 400 DRM_MODE_REFLECT_Y); 401 402 dcss_plane->ch_num = zpos; 403 404 return dcss_plane; 405 } 406