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 struct drm_crtc_state *crtc_state; 271 bool modifiers_present; 272 u32 src_w, src_h, dst_w, dst_h; 273 struct drm_rect src, dst; 274 bool enable = true; 275 276 if (!fb || !state->crtc || !state->visible) 277 return; 278 279 crtc_state = state->crtc->state; 280 modifiers_present = !!(fb->flags & DRM_MODE_FB_MODIFIERS); 281 282 if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state) && 283 !dcss_plane_needs_setup(state, old_state)) { 284 dcss_plane_atomic_set_base(dcss_plane); 285 return; 286 } 287 288 src = plane->state->src; 289 dst = plane->state->dst; 290 291 /* 292 * The width and height after clipping. 293 */ 294 src_w = drm_rect_width(&src) >> 16; 295 src_h = drm_rect_height(&src) >> 16; 296 dst_w = drm_rect_width(&dst); 297 dst_h = drm_rect_height(&dst); 298 299 if (plane->type == DRM_PLANE_TYPE_OVERLAY && 300 modifiers_present && fb->modifier == DRM_FORMAT_MOD_LINEAR) 301 modifiers_present = false; 302 303 dcss_dpr_format_set(dcss->dpr, dcss_plane->ch_num, state->fb->format, 304 modifiers_present ? fb->modifier : 305 DRM_FORMAT_MOD_LINEAR); 306 dcss_dpr_set_res(dcss->dpr, dcss_plane->ch_num, src_w, src_h); 307 dcss_dpr_set_rotation(dcss->dpr, dcss_plane->ch_num, 308 state->rotation); 309 310 dcss_plane_atomic_set_base(dcss_plane); 311 312 dcss_scaler_setup(dcss->scaler, dcss_plane->ch_num, 313 state->fb->format, src_w, src_h, 314 dst_w, dst_h, 315 drm_mode_vrefresh(&crtc_state->mode)); 316 317 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 318 dst.x1, dst.y1, dst_w, dst_h); 319 dcss_dtg_plane_alpha_set(dcss->dtg, dcss_plane->ch_num, 320 fb->format, state->alpha >> 8); 321 322 if (!dcss_plane->ch_num && (state->alpha >> 8) == 0) 323 enable = false; 324 325 dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, enable); 326 dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, enable); 327 328 if (!enable) 329 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 330 0, 0, 0, 0); 331 332 dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, enable); 333 } 334 335 static void dcss_plane_atomic_disable(struct drm_plane *plane, 336 struct drm_plane_state *old_state) 337 { 338 struct dcss_plane *dcss_plane = to_dcss_plane(plane); 339 struct dcss_dev *dcss = plane->dev->dev_private; 340 341 dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, false); 342 dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, false); 343 dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 0, 0, 0, 0); 344 dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, false); 345 } 346 347 static const struct drm_plane_helper_funcs dcss_plane_helper_funcs = { 348 .prepare_fb = drm_gem_fb_prepare_fb, 349 .atomic_check = dcss_plane_atomic_check, 350 .atomic_update = dcss_plane_atomic_update, 351 .atomic_disable = dcss_plane_atomic_disable, 352 }; 353 354 struct dcss_plane *dcss_plane_init(struct drm_device *drm, 355 unsigned int possible_crtcs, 356 enum drm_plane_type type, 357 unsigned int zpos) 358 { 359 struct dcss_plane *dcss_plane; 360 const u64 *format_modifiers = dcss_video_format_modifiers; 361 int ret; 362 363 if (zpos > 2) 364 return ERR_PTR(-EINVAL); 365 366 dcss_plane = kzalloc(sizeof(*dcss_plane), GFP_KERNEL); 367 if (!dcss_plane) { 368 DRM_ERROR("failed to allocate plane\n"); 369 return ERR_PTR(-ENOMEM); 370 } 371 372 if (type == DRM_PLANE_TYPE_PRIMARY) 373 format_modifiers = dcss_graphics_format_modifiers; 374 375 ret = drm_universal_plane_init(drm, &dcss_plane->base, possible_crtcs, 376 &dcss_plane_funcs, dcss_common_formats, 377 ARRAY_SIZE(dcss_common_formats), 378 format_modifiers, type, NULL); 379 if (ret) { 380 DRM_ERROR("failed to initialize plane\n"); 381 kfree(dcss_plane); 382 return ERR_PTR(ret); 383 } 384 385 drm_plane_helper_add(&dcss_plane->base, &dcss_plane_helper_funcs); 386 387 ret = drm_plane_create_zpos_immutable_property(&dcss_plane->base, zpos); 388 if (ret) 389 return ERR_PTR(ret); 390 391 drm_plane_create_rotation_property(&dcss_plane->base, 392 DRM_MODE_ROTATE_0, 393 DRM_MODE_ROTATE_0 | 394 DRM_MODE_ROTATE_90 | 395 DRM_MODE_ROTATE_180 | 396 DRM_MODE_ROTATE_270 | 397 DRM_MODE_REFLECT_X | 398 DRM_MODE_REFLECT_Y); 399 400 dcss_plane->ch_num = zpos; 401 402 return dcss_plane; 403 } 404